Pythonでプレオーダートラバーサルから二分探索木(BST)を構築する方法
与えられた先行順走査(プレオーダートラバーサル)に一致する二分探索木を作成することを考えます。例えば、先行順走査が [8,5,1,7,10,12] の場合、出力は [8,5,10,1,7,null,12] となり、構築される木は以下のようになります。

アルゴリズムの考え方
先行順走査では、最初の要素が必ず根(ルート)になります。また、二分探索木の性質上、あるノードより小さい値は左部分木へ、大きい値は右部分木へ配置されます。この性質を利用し、スタックを使って祖先ノードを管理しながら木を組み立てていくのがポイントです。
手順
- root := 先行順リストの0番目の要素をノードとして作成
- stack := 空のスタックを用意し、rootをプッシュ
- 先行順リストの2番目以降の各要素 i について以下を繰り返す
- i := 値 i を持つ新しいノードを作成
- i の値がスタックトップの値より小さい場合
- スタックトップノードの左の子 := i
- i をスタックにプッシュ
- そうでない場合
- スタックが空でなく、スタックトップの値が i の値より小さい間、以下を繰り返す
- last := スタックトップのノード
- スタックからポップ
- last ノードの右の子 := i
- i をスタックにプッシュ
- スタックが空でなく、スタックトップの値が i の値より小さい間、以下を繰り返す
- root を返す
Pythonでの実装例
理解を深めるために、以下の実装を見てみましょう。
class Solution(object): def bstFromPreorder(self, preorder): """ :type preorder: List[int] :rtype: TreeNode """ root = TreeNode(preorder[0]) stack = [root] for i in preorder[1:]: i = TreeNode(i) if i.val<stack[-1].val: stack[-1].left = i stack.append(i) else: while stack and stack[-1].val<i.val: last = stack.pop(-1) last.right = i stack.append(i) return root
入力例
[8,5,1,7,10,12]
出力例
[8,5,10,1,7,null,12]
計算量について
このアルゴリズムでは、各ノードは最大でも一度プッシュされ、一度ポップされるため、時間計算量は O(n)、空間計算量もスタックの深さ分の O(n) となります。先行順走査の特性とBSTの性質を組み合わせることで、非常に効率的に木を再構築できるのが特徴です。
-
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 の両方を子孫に持ち、それより
-
Pythonでソート済み配列を高さバランスの二分探索木に変換する方法
ソートされた配列 A が与えられたとき、そこから高さバランスの取れた二分探索木(BST)を生成することを考えます。ここで「高さバランスの取れた二分木」とは、すべてのノードにおいて、左右の部分木の深さの差が常に1以下であるような二分木のことを指します。例として、配列が [-10, -3, 0, 5, 9] の場合、出力の一例は [0, -3, 9, -10, null, 5] のようになります。解法のアプローチこの問題は、配列の中央要素をルートに選ぶというシンプルな発想で解くことができます。具体的な手順は以下の通りです。配列 A が空の場合は、Null(None)を返します。配列の中央の要素を見