【科学技術計算講座ミニ】木構造と二分探索木②

前回は木構造と二分探索木についてお話しました。

木構造と二分探索木について解説します。Pythonで二分探索木を実際にプログラミングしながら説明していきます。

今回は二分探索木の特徴について解説します。

二分探索木の計算量

二分探索木はその形がきれいな三角形になっていれば、探索にかかる平均的な計算量は$O(\log n)$になります。ところが、木の形は挿入するデータの順番によって変わってきます。前回のデータの場合は、25→13→20→42→67→4→51→38→1→9の順にデータを挿入すると、下図のような木になりました。

二分探索木

ところが、1→4→9→13→20→25→38→42→51→67 のように整列した順にデータを挿入すると、1をルートとして全てのノードが右に子を持つ木が出来上がります。

二分探索木の最悪パターン

このツリーで67を探索するとルートノードから全てのノードをたどる必要があり、効率的に探索することができません。この場合は最悪$O(n)$の計算量がかかってしまいます。

つまり、効率良い探索を行うためには、木の形が左右に均等に枝分かれしており、きれいな三角形に近い必要があるというわけです。このような効率の良い木を平衡木と呼びます。平衡木は木の高さが$O(\log n)$程度になっています。平衡木にはAVL木赤黒木B木など様々な種類がありますが、今回は詳細は省略します。

木の走査

以前学んだリスト構造は一列に並んでいるため、リストの先頭から順番にたどると全てのノードに対して処理することができました。木構造の場合は枝分かれしているため、全てのノードをたどる手順はいくつか考えられます。ここでは木のたどり方について説明します。

木をたどって行くことを木の走査(traverse)と呼びます。まず、各ノードに対して重複しないで1回ずつ処理するために、ルートノードを起点として木を左側からぐるりと巡る一筆書きのようなルートを考えます。

二分探索木の走査

一筆書きといっても各ノードを複数回通っているため、一回ずつ処理するためにはどの通過時点でノードに立ち寄るかを考えなくてはなりません。これには3つの方法があります。

行きがけ順

まず最初に、そのノードを初めて通過した時点で処理するという方法が考えられます。これを行きがけ順(前順、先行順、preorder)といいます。ノードに立ち寄った時点をポイントで表すと次のように図示できます。

二分探索木の行きがけ順走査

これをプログラミングしてみます。処理としてはノードの値を表示するものとします。前回作った二分探索木のクラスにメソッドprint_preorderとして追加してみましょう。

  def print_preorder(self, node):
    if node == None:
      return
    print(node.value, end=" ")
    self.print_preorder(node.left)     
    self.print_preorder(node.right)  

引数nodeは通過するノードを表しています。ここでプログラムは再帰関数で表現しています。nodeNone、つまり木の末端の空ノードに来ればreturnします。行きがけ順なので、最初にprintでノードの値を出力し、次にノードの左(node.left)をたどり、最後に右(node.right)をたどるようにします。

これで先のデータツリーを表示してみましょう。ルートノードからたどるので、ルートを引数に与えます。

tree.print_preorder(tree.root)  

すると、次のように行きがけ順で出力されます。

25 13 4 1 9 20 42 38 67 51

通りがけ順

次に考えるのは、各ノードの左側をたどった後でそのノードを処理し、それから右側をたどるという順序です。これは通りがけ順(間順、中間順、inorder)と呼ばれます。図で書くと

二分探索木の通りがけ順

となります。木の末端(例えば①)ノードは左右の子ノードを持たないので、通過時点で処理されます。

プログラムはメソッドprint_inorderとして

  def print_inorder(self, node):
    if node == None:
      return
    self.print_inorder(node.left)   
    print(node.value, end=" ")  
    self.print_inorder(node.right) 

と書けます。printの位置が中間に来ています。これも同様に実行すると、

1 4 9 13 20 25 38 42 51 67

となります。

ここで、結果を見るとデータが小さい値から順に整列していることがわかります。つまり、二分探索木を通りがけ順にたどると、ソートされたデータを得ることができます。

帰りがけ順

最後に考えるのは、左側をたどった後、右側をたどり最後にそのノードに立ち寄るという順序です。この方法は帰りがけ順(後順、後行順、postorder)と呼ばれています。

二分探索木の通りがけ順

プログラム(print_postorder)も全く同様でprintの位置が最後になっているだけです。

  def print_postorder(self, node):
    if node == None:
      return
    self.print_postorder(node.left)    
    self.print_postorder(node.right) 
    print(node.value, end=" ")  

実行すると、

1 9 4 20 13 38 51 67 42 25

となります。


全体の目次

スポンサーリンク
科学技術計算のご相談は「キャットテックラボ」へ

科学技術計算やCAEに関するご相談、計算用プログラムの開発などお困りのことは「株式会社キャットテックラボ」へお問い合わせください。

お問い合わせはこちら

フォローする