中順走査(In-order Traversal)による O(n) 実装
二分探索木(BST)において、ちょうど2つのノードの値が誤って入れ替わっている。 木の構造を変更せずに、値のスワップのみでBSTを修復する。
入力: root = [3,1,4,null,null,2] (3と2が入れ替わっている)
出力: [2,1,4,null,null,3]
3* 2
/ \ → / \
1 4 1 4
/ /
2* 3
| ケース | 正しい順序 | 入れ替え後 | 違反回数 |
|---|---|---|---|
| 隣接ノード | [1,2,3,4] | [1,3,2,4] | 1回 (3>2) |
| 非隣接ノード | [1,2,3,4] | [1,4,3,2] | 2回 (4>3, 3>2) |
class Solution:
def recoverTree(self, root: Optional[TreeNode]) -> None:
"""
BST修復(再帰版)
Time: O(n), Space: O(h)
"""
self.first = self.second = self.prev = None
self._inorder(root)
# 値のスワップ
self.first.val, self.second.val = self.second.val, self.first.val
def _inorder(self, node: Optional[TreeNode]) -> None:
if not node:
return
# 左部分木を走査
self._inorder(node.left)
# 違反チェック: prev > curr はBST違反
if self.prev and self.prev.val > node.val:
if not self.first:
self.first = self.prev # 最初の違反
self.second = node # 常に更新
self.prev = node
# 右部分木を走査
self._inorder(node.right)
フローの説明:
1初期化: first, second, prev を None に設定
2ノードがNoneなら即座にreturn(基底条件)
3左部分木を再帰的に走査(中順走査の「左」)
4違反チェック: prev.val > node.val ならBST違反
5最初の違反で first = prev を設定
6常に second = node を更新(隣接/非隣接両対応)
7prev を現在のノードに更新
8右部分木を再帰的に走査(中順走査の「右」)
9全走査完了後、first と second の値をスワップして修復完了
| 方式 | 時間計算量 | 空間計算量 | 特徴 |
|---|---|---|---|
| 再帰版 | O(n) | O(h) | 最も可読性が高い |
| Morris版 | O(n) | O(1) | Follow-up要件を満たす |
| 配列保存版 | O(n) | O(n) | 理解しやすい |