Pythonのスタックを使って後順走査(ポストオーダー)から二分探索木(BST)を構築する方法
問題概要
二分探索木(BST)の後順走査(ポストオーダートラバーサル)の結果が1つ与えられたとき、その走査結果から元となる二分探索木を復元する問題を考えます。
例えば、入力が [6, 12, 10, 55, 45, 15] の場合、出力される木構造は次のようになります。

解法のアプローチ
この問題を解くために、以下の手順に従います。
関数
solve()を定義します。引数として後順走査のリストpostorderを受け取ります。n:=postorderの要素数とします。root:= 後順走査の最後の要素を値として持つ新しいツリーノードを作成します。stk:= 空のスタックを用意します。rootをスタックにプッシュします。i:=n - 2とします。i >= 0の間、以下の処理を繰り返します。x:= 値postorder[i]を持つ新しいノードを作成します。スタックが空でなく、かつ
postorder[i]がスタックの先頭ノードの値より小さい間、次を繰り返します。temp:= スタックの先頭ノードスタックから先頭要素をポップ(削除)します。
tempが null でない場合は、temp.left:=xとします。それ以外の場合は、スタックの先頭ノードの右側に
xを設定します。xをスタックにプッシュします。i:=i - 1とします。
rootを返します。メイン側では、
solve(postorder)の結果を返すだけで完了です。
なぜこの方法が機能するのか
後順走査では「左部分木 → 右部分木 → 根」の順にノードを訪問します。したがって、配列の最後の要素が必ず木全体の根になります。さらに、配列を末尾から先頭へ向かって処理すると、まず根から右端の経路に沿ったノード群が現れ、その後に小さな値(左部分木のノード)が出現します。スタックを使うことで、「どのノードの子として接続すべきか」という判断を効率的に行えます。各ノードがスタックに出入りするのは高々2回なので、時間計算量は O(n) と非常に効率的です。
実装例
理解を深めるために、以下のPython実装を見てみましょう。
class TreeNode:
def __init__(self, data = 0):
self.val = data
self.left = None
self.right = None
def solve(postorder):
n = len(postorder)
root = TreeNode(postorder[n - 1])
stk = []
stk.append(root)
i = n - 2
while ( i >= 0):
x = TreeNode(postorder[i])
temp = None
while (len(stk) > 0 and postorder[i] < stk[-1].val) :
temp = stk[-1]
stk.pop()
if (temp != None):
temp.left = x
else:
stk[-1].right = x
stk.append(x)
i = i - 1
return root
def build_tree(postorder):
return solve(postorder)
def inord( node):
if node:
inord(node.left)
print( node.val, end = " ")
inord(node.right)
postorder = [6, 12, 10, 55, 45, 15]
root = build_tree(postorder)
print( "Inorder traversal:", end = " ")
inord(root)
入力
[6, 12, 10, 55, 45, 15]
出力
6 10 12 15 45 55
まとめ
復元された木に対して中順走査(インオーダートラバーサル)を行うと、6 10 12 15 45 55 という昇順の結果が得られます。これは二分探索木の性質(中順走査すると昇順に並ぶ)と一致しており、正しく木が復元できたことが確認できます。スタックを活用したこの手法は、再帰呼び出しを使わずに線形時間で BST を構築できる優れたアプローチです。
-
Pythonで二分木の中間順トラバーサル(Inorder Traversal)を再帰なしで実装する方法
二分木(バイナリツリー)が与えられたとき、再帰を使わずに中間順トラバーサル(Inorder Traversal)で木を走査する方法を解説します。例えば、次のような二分木があるとします。この木に対して中間順トラバーサルを行うと、結果は [2,5,7,10,15,20] のようになります。中間順トラバーサルは「左の子 → 親ノード → 右の子」の順にノードを訪問するため、二分探索木の場合は値が昇順に出力されるという特徴があります。アルゴリズムの考え方再帰を使わずにスタックを活用することで、この問題を解決できます。手順は以下のとおりです。結果を格納する配列 res と、ノードを一時的に保持するスタッ
-
Pythonのリストをスタックとキューとして使う方法を徹底解説
本記事では、Python 3.x(およびそれ以前のバージョン)におけるスタック(Stack)とキュー(Queue)という基本的なデータ構造について解説します。それぞれのデータ構造の仕組みや操作方法を、実際のコード例とともにわかりやすく学んでいきましょう。 本記事で扱う主なトピックは以下の通りです。 挿入操作(Push / Enqueue) 削除操作(Pop / Dequeue) 表示・走査(トラバース)操作 前提知識:リストとリスト操作の基礎関連するデータ構造:リスト操作 スタック(Stack)とは スタックでは、オブジェクトが積み重なるように格納され、取り出す際には到着した順序とは逆の順