第五回-05 : 二分木の実装と利用

ここでは、二分木のソースコードを解説する。

やや長いのでまずは zip ファイルをダウンロードして欲しい。
このファイルを展開すると、BCC Developer のプロジェクトファイル btree.bdp が入っている。
このファイルを BCC Developer で開いて欲しい。

正しく開くと BTree.h (クラス宣言部)、BTree.cpp (クラス実現部)、btree_test.cpp (クラス利用部) の 3 つの階層構造が BCC Developer の左側に見えるであろう。

BTree.h と BTree.cpp には BNode クラスと BTree クラス (binary tree の略) が記述されている。
これらのクラスの解説は後回しにし、まずは利用部 btree_test.cpp を見てその利用法から解説してゆこう。

二分木のソースコード~利用部

二分木の利用部である btree_test.cpp において、二分木は

BTree btree;


のように宣言されている。

この二分木に、前のページの「学籍番号&成績」のデータを順に格納しているのが

  btree.insert(5,'B');
  btree.insert(2,'A');
  btree.insert(1,'D');
  btree.insert(4,'F');
  btree.insert(8,'A');
  btree.insert(9,'C');


の行である。この命令により、前のページの二分木が完成する (はずである)。
ただし、この BTree のソースは (みなさんに記述もらうための) 空欄があるので完全な二分木は完成しない。
具体的には、「左側にはデータを格納できるが、右側には (まだ) データを格納できない」。 そのため、できあがる (不完全な) 二分木は下の図のようになる。



さて、格納したデータを取り出すにはいくつかの方法がある。

格納した全てのデータをイテレータで辿ってゆく方法はこちら。

  for(btree.setItrBegin() ; !btree.ItrEqual(0) ; ++btree){
    // イテレータを、begin() から end() まで移動させるための記法

     btree.Itr()->PrintLocal();
      // イテレータが指す Node に対し PrintLocal() 関数を呼び出す
  }

[実行結果]
1 D
2 A
5 B


(不完全な) 二分木に格納された 3 つのデータが表示されていることがわかる。
「btree.Itr()」が現在のノードを指すポインタとなり、PrintLocal 関数はその Node の学籍番号と成績を表示する。

次に、ある学籍番号の学生の成績のみを取り出すのはこちら。

  // key=5 に対応する data の探索
  cout << "key=5, data=" << btree.find(5) << "\n";

[実行結果]
key=5, data=B


「学籍番号 5 」を手がかり (key) にして、二分木を探索し、求める成績を得ている (btree.find(5)) 。

最後に、BTree を prioirty queue として用いる場合の記述例はこちら。

while(btree.nodeNum()>0){

  cout << btree.getTop() << "\n";

  btree.deleteTop();
}

[実行結果]
D
A
B


while 文によって二分木の中のデータがなくなるまでデータを取り出し続ける。
「btree.getTop()」は、もっとも学籍番号が小さい学生の成績を取り出す命令。
「btree.deleteTop()」は、取り出した成績を削除する命令である。

さて、プロジェクトをメイクし、プログラムを実行してみよう。

----data access with the iterator
1 D
2 A
5 B
----data access with the key
key=5, data=B
----used as a Priority Queue
D
A
B


と表示され、上記 3 つの利用例の結果が表示されるのがわかる。

もちろん、これは右側の枝が存在しない不完全な二分木であり、完全な二分木は 演習問題を解くことで完成する。

二分木のソースコード~実現部

BTree.cpp の解説をするが、全てを理解するのは時間的にもレベル的にもやや難しいので、
まずはこちらの提示する部分のみを理解するよう努力して欲しい。

まず、BTree クラスと BNode クラスの関係を図示すると、下図のようになる。



BNode クラスがノードであり、学籍番号 key、 成績 data、
左下の BNode へのポインタ leftNode、右下の Bnode へのポインタ rightNode、 上の BNode へのポインタ parentNode をメンバとして持つ。

BTree クラスは root ノード (へのポインタ) をメンバとしてもつ。

BTree.cpp の中で次のような部分を探して欲しい (BCC Developer の検索機能を使うと良いだろう)。

bool BNode::insert(int k, char d){

  if(k<key){   // k < key なら

    bool result;     // 挿入されたかどうかの結果を格納する変数

    if(leftNode != 0){               // 左の枝に BNode が存在すれば
      result = leftNode->insert(k,d);
    }else{                           // 左の枝に BNode が存在しなければ
      leftNode = new BNode(k,d);     //新たな BNode を追加
      leftNode->parentNode = this;   //自分が新たな BNode の上位に
      result = true;                 // 挿入されたので true
    }

    return result;   // 挿入されたかどうかを return

  }else{             // k >= key なら
…


これは、ノードの左下に新たなノードを加えるための関数 insert を示している。
key は現在のノードのキー (学籍番号)、k は新たに加わるノードのキー (学籍番号) である。
「k<key」であれば、自分のノードの左下にノードが加わることになる。

ただし、左下に既にノードが存在すれば、その更に下に探索を続ける (insert 関数を再帰的に呼び出す)
左下にノードが存在しなければ、new 演算子であらたなノードを加えることになる。

もう一つ、次のような部分を探してみよう。

char BNode::find(int k){  // key=k なる Node を探し, data を返す関数

   if(this==0){    // この Node が存在しない (ヌルポインタである) ならば
     return(0);
   }else{
     if(key==k){        // この Node が求める Node なら
       return(data);
     }else if(k < key){ // この Node より左の Node が求める Node なら
       return(leftNode->find(k));
     }else{	
…


これは、ある学籍番号に対応する学生の成績を取得する関数である。 現在のノードの学籍番号 (key) が求める学生の学籍番号 (k) に等しければ、 データメンバ data をそのまま return すれば良い。
「k<key」なら、枝の左側をさらに探索する (find 関数の再帰的呼び出し)



←第五回-04 : 二分木の概念第五回課題→

第五回トップページへ

クラスから入る C++ へ戻る