Skip to content

Commit cc3ba41

Browse files
authored
Merge branch 'main' into dependabot/pip/pip-16abd79c16
2 parents d91b1d3 + 81952ab commit cc3ba41

28 files changed

Lines changed: 19340 additions & 11 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Binary Tree Inorder Traversal — Rust Edition
2+
3+
## 1. 問題の分析
4+
5+
### 競技プログラミング視点での分析
6+
7+
- 全ノードを一度だけ訪れる **O(N) 時間・O(N) 空間**が理論下界
8+
- LeetCode の Rust 環境では `TreeNode``Option<Rc<RefCell<TreeNode>>>` でラップされており、**所有権の移動なしに借用で読み取る** 設計が必須
9+
- 明示スタックによる反復実装でコールスタック消費を排除
10+
11+
### 業務開発視点での分析
12+
13+
- `Option<Rc<RefCell<TreeNode>>>` という複合型を安全に扱うため、`.borrow()` による共有参照と `Option``?`/`if let` による null 安全な展開が鍵
14+
- `Vec<i32>` への追記は `push` のみで副作用をローカルに限定 → Pure function に近い設計
15+
- `Result` は不要(入力が空 = 空ベクタを返すだけで、エラー状態がない)
16+
17+
### Rust特有の考慮点
18+
19+
- `Rc<RefCell<T>>` の共有所有権モデル:`clone()` はポインタのコピーのみ(O(1))
20+
- `.borrow()``Ref<TreeNode>` を取得 → 借用スコープを最小化してデッドロック回避
21+
- スタックの型を `Rc<RefCell<TreeNode>>` とすることで、非 null 要素のみを格納できる
22+
23+
---
24+
25+
## 2. アルゴリズムアプローチ比較
26+
27+
| アプローチ | 時間計算量 | 空間計算量 | Rust実装コスト | 安全性 | 可読性 | 備考 |
28+
| --------------------------- | ---------- | ---------- | -------------- | ------ | ------ | --------------------------------------------------- |
29+
| **A: 再帰 DFS** | O(N) | O(N) ||| 最高 | コールスタック深さN、深い木でスタックオーバーフロー |
30+
| **B: 反復(明示スタック)** | O(N) | O(N) |||| Follow-up要件を満たす。`Rc::clone`でO(1)コピー |
31+
| **C: Morris Traversal** | O(N) | O(1) | 非常に高 ||| `RefCell`の可変借用が複数箇所で必要、実装困難 |
32+
33+
---
34+
35+
## 3. 選択したアルゴリズムと理由
36+
37+
- **選択したアプローチ**: **B: 反復(明示スタック)**
38+
- **理由**:
39+
- Follow-upの反復解要件を満たす
40+
- `Rc<RefCell<T>>` モデルで Morris は `borrow_mut()` の多重借用を誘発しやすく危険
41+
- `Rc::clone()` はポインタカウントのインクリメントのみで、ノード値コピーなし
42+
- スタック `Vec<Rc<RefCell<TreeNode>>>` で型安全かつ非 null 要素のみ管理
43+
44+
- **Rust特有の最適化ポイント**:
45+
- `Rc::clone(&node)` の明示的クローンでコスト意識を表現
46+
- `.borrow()` の借用スコープを `{}` ブロックで最小化(借用の早期解放)
47+
- `Vec::with_capacity` でリアロケーション回数を削減(N≤100 なので省略可)
48+
49+
---
50+
51+
## 4. 実装コード
52+
53+
```rust
54+
// Runtime 0 ms
55+
// Beats 100.00%
56+
// Memory 2.19MB
57+
// Beats 74.02%
58+
59+
// leetcode環境では use std::rc::Rc; use std::cell::RefCell; が提供済み
60+
61+
// ---- メイン実装 ----
62+
63+
/// Binary Tree Inorder Traversal(反復・明示スタック実装)
64+
///
65+
/// アルゴリズム:
66+
/// 1. current カーソルを根から開始
67+
/// 2. current が Some の間、左端までスタックに積む(Rc::clone でポインタコピー)
68+
/// 3. スタックから pop → 値を記録 → current を右子に移す
69+
/// 4. current と stack が両方空になったら終了
70+
///
71+
/// # Arguments
72+
/// * `root` - 二分木の根ノード(`None` = 空木)
73+
///
74+
/// # Returns
75+
/// 中順走査の値ベクタ(空木の場合は空ベクタ)
76+
///
77+
/// # Complexity
78+
/// - Time: O(N) — 全ノードを一度だけ訪問
79+
/// - Space: O(N) — 明示スタック最大深さ N(最悪: 左に偏った木)
80+
pub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {
81+
// 結果バッファ(N ≤ 100 なのでデフォルト容量で十分)
82+
let mut result: Vec<i32> = Vec::new();
83+
84+
// 明示スタック:非 null ノードのみ格納(型レベルで None 混入を排除)
85+
let mut stack: Vec<Rc<RefCell<TreeNode>>> = Vec::new();
86+
87+
// カーソル:Option で「未訪問ノードあり」「なし」を型安全に表現
88+
let mut current: Option<Rc<RefCell<TreeNode>>> = root;
89+
90+
// current(未訪問)とstack(保留)のいずれかが残る間ループ
91+
while current.is_some() || !stack.is_empty() {
92+
93+
// ---- フェーズ1: 左端まで潜りながらスタックに積む ----
94+
while let Some(node_rc) = current {
95+
// Rc::clone はポインタカウントのインクリメントのみ(O(1)、コピーコストなし)
96+
stack.push(Rc::clone(&node_rc));
97+
98+
// .borrow() スコープを最小化: 左子のクローンを取得したら即解放
99+
let left = node_rc.borrow().left.clone();
100+
current = left; // 左へ進む(None なら次のwhileを脱出)
101+
}
102+
103+
// ---- フェーズ2: スタック top を取り出して訪問 ----
104+
// stack.is_empty() でないことはループ条件で保証済み → unwrap 安全
105+
if let Some(node_rc) = stack.pop() {
106+
// 借用スコープを {} で明示的に限定(右子取得前に解放)
107+
let (val, right) = {
108+
let node = node_rc.borrow(); // Ref<TreeNode>
109+
(node.val, node.right.clone())
110+
}; // ← ここで Ref が drop され、借用解放
111+
112+
// 中順で値を記録
113+
result.push(val);
114+
115+
// ---- フェーズ3: 右部分木へカーソルを移す ----
116+
current = right; // None なら次ループで即 pop フェーズへ
117+
}
118+
}
119+
120+
result
121+
}
122+
```
123+
124+
---
125+
126+
## 5. アルゴリズム動作トレース
127+
128+
`root = [1, null, 2, 3]` を例に各フェーズを可視化します。
129+
130+
```
131+
ツリー構造:
132+
1
133+
\
134+
2
135+
/
136+
3
137+
```
138+
139+
| ステップ | current | stack(底→top) | result | 操作 |
140+
| -------- | ------------ | --------------- | ------- | --------------------------------- |
141+
| 初期 | Some(1) | [] | [] ||
142+
| Ph1 | None | [1] | [] | 1 を push、left=None で停止 |
143+
| Ph2 || [] | [1] | pop→1、val=1 を記録 |
144+
| Ph3 | Some(2) | [] | [1] | current = right(2) |
145+
| Ph1 | Some(3)→None | [2,3] | [1] | 2 push → 3 push、left=None で停止 |
146+
| Ph2 || [2] | [1,3] | pop→3、val=3 を記録 |
147+
| Ph3 | None | [2] | [1,3] | current = right(None) |
148+
| Ph2 || [] | [1,3,2] | pop→2、val=2 を記録 |
149+
| Ph3 | None | [] | [1,3,2] | ループ終了 |
150+
151+
**Output: `[1, 3, 2]`**
152+
153+
---
154+
155+
## Rust固有の最適化観点まとめ
156+
157+
| 観点 | 本実装での適用 |
158+
| ----------------- | ---------------------------------------------------------------------- |
159+
| **所有権管理** | `Rc::clone` でポインタ共有(ノード値コピーなし) |
160+
| **借用の最小化** | `{ let node = node_rc.borrow(); ... }` で借用スコープを即解放 |
161+
| **null 安全性** | `Option<Rc<RefCell<TreeNode>>>` + `while let` で None を型レベルで排除 |
162+
| **Pure function** | 入力ツリーへの書き込みゼロ(`.borrow()` のみ、`.borrow_mut()` 不使用) |
163+
| **パニック制御** | `unwrap()` を排除し `if let` / `while let` で安全展開 |
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Binary Tree Inorder Traversal — Python Edition
2+
3+
## 1. 問題分析結果
4+
5+
### 競技プログラミング視点
6+
7+
- **制約分析**: N ≤ 100 と極小。O(N) 時間・O(N) 空間が理論下界
8+
- **最速手法**: 明示スタックによる反復実装。Python の関数呼び出しオーバーヘッドを回避
9+
- **CPython最適化**: `list.append()` は C 実装で O(1) 均償。`list.pop()` も同様
10+
11+
### 業務開発視点
12+
13+
- **型安全設計**: `Optional[TreeNode]``list[int]` で Pylance 完全対応
14+
- **エラーハンドリング**: 空木(`None`)はガード節で即 `[]` を返し、例外を発生させない
15+
- **可読性**: フェーズをコメントで明示し、意図を self-documenting に
16+
17+
### Python特有分析
18+
19+
- **データ構造選択**: スタックは `list``append`/`pop` ともに O(1) で deque 不要)
20+
- **再帰 vs 反復**: Python のデフォルト再帰上限は 1,000。反復実装が本番安全
21+
- **`while` + `list.pop()`**: CPython の C レイヤーで動作し最速
22+
23+
---
24+
25+
## 2. 採用アルゴリズムと根拠
26+
27+
- **選択**: 反復(明示スタック)+ カーソルポインタ方式
28+
- **Python最適化戦略**: `list.append` / `list.pop` を直接呼び出し、属性ルックアップを最小化
29+
- **トレードオフ**: 再帰より数行多いが、スタックオーバーフロー耐性と Follow-up 要件を同時に満たす
30+
31+
---
32+
33+
## 3. 実装パターン
34+
35+
### 業務開発版(型安全・可読性重視)
36+
37+
```python
38+
# Runtime 0 ms
39+
# Beats 100.00%
40+
# Memory 19.22 MB
41+
# Beats 85.80%
42+
43+
# Definition for a binary tree node.
44+
# class TreeNode:
45+
# def __init__(self, val=0, left=None, right=None):
46+
# self.val = val
47+
# self.left = left
48+
# self.right = right
49+
50+
from __future__ import annotations
51+
from typing import Optional
52+
53+
54+
class Solution:
55+
"""
56+
Binary Tree Inorder Traversal
57+
中順走査(左 → 根 → 右)を反復スタックで実装。
58+
Follow-up: 再帰を使わない反復解。
59+
"""
60+
61+
def inorderTraversal(self, root: Optional[TreeNode]) -> list[int]:
62+
"""
63+
二分木の中順走査を反復で実行する。
64+
65+
Args:
66+
root: 二分木の根ノード(None = 空木)
67+
68+
Returns:
69+
中順走査の値リスト。空木の場合は空リスト。
70+
71+
Complexity:
72+
Time: O(N) — 全ノードを一度だけ訪問
73+
Space: O(N) — 明示スタックの最大深さ(最悪: 左偏木)
74+
"""
75+
# ガード: 空木は即座に空リストを返す
76+
if root is None:
77+
return []
78+
79+
result: list[int] = []
80+
stack: list[TreeNode] = []
81+
current: Optional[TreeNode] = root
82+
83+
while current is not None or stack:
84+
85+
# ── フェーズ1: 左端まで潜りながらスタックに積む ──────────────
86+
while current is not None:
87+
stack.append(current) # 右・自身は後回し
88+
current = current.left # 左へ進む
89+
90+
# ── フェーズ2: スタック top を取り出して訪問 ─────────────────
91+
# ループ条件より stack が空でないことは保証済み
92+
node: TreeNode = stack.pop()
93+
result.append(node.val) # ← 中順で値を記録
94+
95+
# ── フェーズ3: 右部分木へカーソルを移す ──────────────────────
96+
current = node.right # None なら次ループで即 pop フェーズへ
97+
98+
return result
99+
```
100+
101+
---
102+
103+
### 競技プログラミング版(性能最優先)
104+
105+
```python
106+
from typing import Optional
107+
108+
109+
class Solution:
110+
def inorderTraversal(self, root: Optional[TreeNode]) -> list[int]:
111+
"""
112+
中順走査 反復実装(最速版)
113+
114+
Time: O(N)
115+
Space: O(N)
116+
"""
117+
res: list[int] = []
118+
stk: list[TreeNode] = []
119+
cur = root
120+
121+
while cur or stk:
122+
# 左端まで積む
123+
while cur:
124+
stk.append(cur)
125+
cur = cur.left
126+
# 訪問 → 右へ
127+
cur = stk.pop()
128+
res.append(cur.val)
129+
cur = cur.right
130+
131+
return res
132+
```
133+
134+
---
135+
136+
## 4. アルゴリズムアプローチ比較
137+
138+
| アプローチ | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | 標準ライブラリ活用 | CPython最適化 | 備考 |
139+
| --------------------------- | ---------- | ---------- | ---------------- | ------ | ------------------ | ------------- | --------------------------------------------------- |
140+
| **A: 再帰 DFS** | O(N) | O(N) || ★★★ || 不適 | 再帰上限 1,000 でスタックオーバーフローリスク |
141+
| **B: 反復(明示スタック)** | O(N) | O(N) || ★★★ | `list` || Follow-up 要件を満たす。本実装 ✅ |
142+
| **C: Morris Traversal** | O(N) | O(1) || ★☆☆ || 不適 | ノード書き換えで副作用あり、Python では実装コスト高 |
143+
144+
---
145+
146+
## 5. 動作トレース
147+
148+
`root = [1, null, 2, 3]` を例に。
149+
150+
```
151+
ツリー:
152+
1
153+
\
154+
2
155+
/
156+
3
157+
```
158+
159+
| ステップ | current | stack(底→top) | result | 操作 |
160+
| -------- | ------- | --------------- | --------- | --------------------------- |
161+
| 初期 | 1 | [] | [] ||
162+
| Ph1 | None | [1] | [] | 1 を push、left=None で停止 |
163+
| Ph2 || [] | [1] | pop→1、val=1 を記録 |
164+
| Ph3 | 2 | [] | [1] | current = right(2) |
165+
| Ph1 | 3→None | [2, 3] | [1] | 2 push → 3 push |
166+
| Ph2 || [2] | [1, 3] | pop→3、val=3 を記録 |
167+
| Ph3 | None | [2] | [1, 3] | current = right(None) |
168+
| Ph2 || [] | [1, 3, 2] | pop→2、val=2 を記録 |
169+
| Ph3 | None | [] | [1, 3, 2] | ループ終了 |
170+
171+
**Output: `[1, 3, 2]`**
172+
173+
---
174+
175+
## Python固有の最適化観点まとめ
176+
177+
| 観点 | 本実装での適用 |
178+
| -------------------- | -------------------------------------------------------------- |
179+
| **Pylance 型安全** | `Optional[TreeNode]``list[int]``list[TreeNode]` で完全対応 |
180+
| **CPython 最速操作** | `list.append` / `list.pop` は C 実装 O(1) 均償 |
181+
| **再帰回避** | 明示スタックでデフォルト再帰上限(1,000)の制約を完全回避 |
182+
| **Pure function** | 入力ツリーへの書き込みゼロ、副作用なし |
183+
| **エッジケース** | `root is None` ガードで空木を即返却、例外発生なし |

0 commit comments

Comments
 (0)