Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで前順走査と中間順走査の結果から二分木を構築する方法


二分木の中間順走査(inorder)前順走査(preorder)の結果が与えられたとき、それらをもとに元の二分木を復元することを考えます。

例えば、前順走査の結果が [3,9,20,15,7]、中間順走査の結果が [9,3,15,20,7] である場合、構築される二分木は次のようになります。

Pythonで前順走査と中間順走査の結果から二分木を構築する方法

アルゴリズムの考え方

この問題は再帰を使うことで簡潔に解けます。鍵となるのは以下の2つの性質です。

  • 前順走査の最初の要素は必ず根(ルート)である
  • 中間順走査において、ルートより左側の要素は左部分木、右側の要素は右部分木に属する

処理の手順

  1. buildTree メソッドに前順走査リスト(preorder)と中間順走査リスト(inorder)を渡します。
  2. preorder の先頭要素を取り出してルートノードとし、preorder からその要素を削除します。
  3. inorder 内でルートの値が位置するインデックス(root_index)を求めます。
  4. 左の子には、inorder の 0 〜 root_index - 1 の範囲を渡して buildTree を再帰呼び出しした結果を設定します。
  5. 右の子には、inorder の root_index + 1 〜 末尾までの範囲を渡して buildTree を再帰呼び出しした結果を設定します。

実装例

以下に Python での実装例を示します。

class TreeNode:
    def __init__(self, data, left = None, right = None):
        self.data = data
        self.left = left
        self.right = right
def print_tree(root):
    # 中間順走査で出力する
    if root is not None:
        print_tree(root.left)
        print(root.data, end = ',')
        print_tree(root.right)
class Solution(object):
    def buildTree(self, preorder, inorder):
        if inorder:
            root = TreeNode(preorder.pop(0))
            root_index = inorder.index(root.data)
            root.left = self.buildTree(preorder,inorder[:root_index])
            root.right = self.buildTree(preorder,inorder[root_index+1:])
            return root
ob1 = Solution()
print_tree(ob1.buildTree([3,9,20,15,7], [9,3,15,20,7]))

入力

[3,9,20,15,7]
[9,3,15,20,7]

出力

9, 3, 15, 20, 7,

計算量と最適化のヒント

この実装では、inorder.index() による線形探索が再帰のたびに行われるため、木が偏っている場合の最悪時間計算量は O(n²) になります。あらかじめ「値 → インデックス」の対応を辞書(ハッシュマップ)に記録しておけば、探索を O(1) にでき、全体を O(n) まで高速化できます。また、pop(0) やスライス操作も O(n) のコストがかかるため、大規模な入力を扱う場合はインデックスを管理する方式へ書き換えるのが効果的です。

  1. Pythonで二分木の中間順トラバーサル(Inorder Traversal)を再帰なしで実装する方法

    二分木(バイナリツリー)が与えられたとき、再帰を使わずに中間順トラバーサル(Inorder Traversal)で木を走査する方法を解説します。例えば、次のような二分木があるとします。この木に対して中間順トラバーサルを行うと、結果は [2,5,7,10,15,20] のようになります。中間順トラバーサルは「左の子 → 親ノード → 右の子」の順にノードを訪問するため、二分探索木の場合は値が昇順に出力されるという特徴があります。アルゴリズムの考え方再帰を使わずにスタックを活用することで、この問題を解決できます。手順は以下のとおりです。結果を格納する配列 res と、ノードを一時的に保持するスタッ

  2. Pythonで二分木を反転する方法:再帰を使った実装を解説

    二分木の反転とは二分木が与えられたとき、その左右の子ノードを入れ替えて「鏡像」のような木を作ることを二分木の反転(Invert Binary Tree)と呼びます。これはアルゴリズムの学習やコーディング面接でも頻出のトピックです。例えば、次のような二分木があったとします。これを反転すると、すべてのノードの左部分木と右部分木が入れ替わり、以下のような木になります。解き方:再帰的アプローチこの問題は再帰を使うと非常にシンプルに解けます。考え方は以下の3ステップです。ルートが None(null)であれば、そのまま返す(ベースケース)現在のノードの左ポインタと右ポインタを入れ替える左部分木と右部分木