Pythonで中順走査(インオーダー)と後順走査(ポストオーダー)から二分木を構築する方法
はじめに
二分木の中順走査(インオーダー)と後順走査(ポストオーダー)の結果が分かっていれば、この2つの列を組み合わせることで元の二分木を一意に復元できます。
例として、後順走査の列が [9,15,7,20,3]、中順走査の列が [9,3,15,20,7] である場合、構築される二分木は次の構造になります。
3
/ \
9 20
/ \
15 7
アルゴリズムの手順
再帰的に木を組み立てていきます。ここではメソッド名を buildTree とし、基本の流れは以下のとおりです。
- 根の決定: 後順走査の「最後の要素」が必ず根(ルート)になります。これを取り出してノードを作成します。
- 分割点の特定: 根の値が中順走査の列のどこに位置するか(root_index)を調べます。
- 左右の分割: 中順走査において root_index より左側の要素は左部分木、右側の要素は右部分木に属します。
- 右部分木の構築: 後順走査は末尾側から消費するため、先に右部分木を再帰的に構築します。
- 左部分木の構築: 続けて左部分木を再帰的に構築します。
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())
# 中順走査内での根の位置を特定
root_index = inorder.index(root.data)
# 右部分木を先に構築(末尾から消費するため)
root.right = self.buildTree(inorder[root_index + 1:], postorder)
# 左部分木を構築
root.left = self.buildTree(inorder[:root_index], postorder)
return root
ob1 = Solution()
print_tree(ob1.buildTree([9, 3, 15, 20, 7], [9, 15, 7, 20, 3]))
入力
中順走査(inorder): [9, 3, 15, 20, 7] 後順走査(postorder): [9, 15, 7, 20, 3]
出力
9, 3, 15, 20, 7,
構築した木を中順走査で出力すると、入力として与えた中順走査の列と一致していることが確認できます。
計算量について
この実装では、再帰呼び出しごとに list.index() による線形探索とスライスによるリストコピーが発生するため、時間計算量は O(n²) となります。
要素の値からインデックスへの対応をあらかじめ辞書(ハッシュマップ)に記録しておき、スライスの代わりに開始・終了インデックスを引数として渡す方式に変更すれば、全体を O(n) まで高速化できます。大きな入力を扱う場合は、この最適化を検討するとよいでしょう。
-
Pythonで二分木の中間順トラバーサル(Inorder Traversal)を再帰なしで実装する方法
二分木(バイナリツリー)が与えられたとき、再帰を使わずに中間順トラバーサル(Inorder Traversal)で木を走査する方法を解説します。例えば、次のような二分木があるとします。この木に対して中間順トラバーサルを行うと、結果は [2,5,7,10,15,20] のようになります。中間順トラバーサルは「左の子 → 親ノード → 右の子」の順にノードを訪問するため、二分探索木の場合は値が昇順に出力されるという特徴があります。アルゴリズムの考え方再帰を使わずにスタックを活用することで、この問題を解決できます。手順は以下のとおりです。結果を格納する配列 res と、ノードを一時的に保持するスタッ
-
Pythonで二分木を反転する方法:再帰を使った実装を解説
二分木の反転とは二分木が与えられたとき、その左右の子ノードを入れ替えて「鏡像」のような木を作ることを二分木の反転(Invert Binary Tree)と呼びます。これはアルゴリズムの学習やコーディング面接でも頻出のトピックです。例えば、次のような二分木があったとします。これを反転すると、すべてのノードの左部分木と右部分木が入れ替わり、以下のような木になります。解き方:再帰的アプローチこの問題は再帰を使うと非常にシンプルに解けます。考え方は以下の3ステップです。ルートが None(null)であれば、そのまま返す(ベースケース)現在のノードの左ポインタと右ポインタを入れ替える左部分木と右部分木