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

Pythonで中順・後順走査の結果から二分木を再構築する方法


二分木の中順走査(inorder)後順走査(postorder)の結果が与えられているとします。これらの走査結果をもとに、元の二分木を復元することを考えます。たとえば、後順走査が [9,15,7,20,3]、中順走査が [9,3,15,20,7] の場合、構築される木は次の図のようになります。

Pythonで中順・後順走査の結果から二分木を再構築する方法

この問題を解く鍵は、後順走査の最後の要素が必ず木のルートになるという性質です。さらに、中順走査においてそのルートより左側にある要素は左部分木に、右側にある要素は右部分木に属します。この性質を再帰的に適用することで、木全体を組み立てることができます。

アルゴリズムの手順

build_tree() メソッドを定義し、中順走査リスト inorder と後順走査リスト postorder を引数として受け取ります。

  • 中順リストが空でない場合、以下を実行します。

    • root := 後順リストの末尾の値でノードを作成し、その要素をリストから取り除く(pop)

    • ind := 中順リスト内での root のデータのインデックス位置

    • root の右の子 := build_tree(inorder[ind+1:], postorder) — 右部分木を先に構築

    • root の左の子 := build_tree(inorder[:ind], postorder) — 左部分木を構築

  • root を返す

ここで重要なのは、右部分木を先に構築する点です。後順走査は「左 → 右 → 根」の順でノードを訪問するため、リストの末尾から取り出すと「根 → 右 → 左」の順になります。つまり、pop() で取り出した直後の要素は、必ず右部分木のノードに対応するのです。

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, inorder, postorder):
        if inorder:
            root = TreeNode(postorder.pop())
            ind = inorder.index(root.data)
            root.right = self.buildTree(inorder[ind+1:],postorder)
            root.left = self.buildTree(inorder[:ind],postorder)
            return root

ob1 = Solution()
print_tree(ob1.buildTree([9,3,15,20,7], [9,15,7,20,3]))

入力

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

出力

[9,3,15,20,7]

計算量と改善のヒント

この実装では、各再帰呼び出しのたびに inorder.index() でルートの位置を線形探索しているため、最悪の場合の時間計算量は O(n²) となります。事前に「値 → インデックス」の対応を辞書(ハッシュマップ)に記録しておけば、位置の検索を O(1) で行えるようになり、全体の計算量を O(n) まで改善できます。要素数が多い木を扱う場合は、この最適化を検討するとよいでしょう。


  1. Pythonで二分探索木の最小共通祖先(LCA)を求める方法【再帰アルゴリズム解説】

    最小共通祖先(LCA)とは二分探索木(Binary Search Tree)が与えられたとき、指定された2つのノードの最小共通祖先(Lowest Common Ancestor:LCA)を求める問題を考えてみましょう。ノード p と q の LCA とは、「p と q の両方を子孫として持つノードの中で、最も深い位置にあるノード」のことです。例えば、次のような二分木があるとします。[6, 2, 8, 0, 4, 7, 9, null, null, 3, 5]この木は以下のような構造になります。この場合、2 と 8 の LCA は 6 となります。6 は 2 と 8 の両方を子孫に持ち、それより

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

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