-
Notifications
You must be signed in to change notification settings - Fork 0
Add LeetCode 102 Binary Tree Level Order Traversal solutions #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2d504a5
feat: add TypeScript, Rust, and Python solutions for LeetCode 102 Bin…
myoshi2891 e868f02
fix(Trees): fix zero val bug in Python solution and add READMEs for 1…
myoshi2891 3e0ca80
build: auto-generate public directory
myoshi2891 2ffb28f
fix(Trees): optimize TS BFS performance and restructure documentation…
myoshi2891 abc2a16
Merge branch 'dev-from-macmini' of github.com:myoshi2891/AlgorithmStu…
myoshi2891 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,3 +102,5 @@ credentials.json | |
| # Node.js関連 | ||
| node_modules/ | ||
| package-lock.json | ||
|
|
||
| .claude/skills | ||
341 changes: 341 additions & 0 deletions
341
.... Binary Tree Level Order Traversal/Binary_Tree_Level_Order_Traversal_Python.md
Large diffs are not rendered by default.
Oops, something went wrong.
352 changes: 352 additions & 0 deletions
352
...02. Binary Tree Level Order Traversal/Binary_Tree_Level_Order_Traversal_Rust.md
Large diffs are not rendered by default.
Oops, something went wrong.
146 changes: 146 additions & 0 deletions
146
...nary Tree Level Order Traversal/Binary_Tree_Level_Order_Traversal_Typescript.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| > 🎯 **[algo-beginner スキル発火]** | ||
| > 言語/カテゴリ: TypeScript | ||
| > 適用ルールセット: 共通5ルール + TS固有5ルール | ||
| > 参照ファイル: references/common.md + references/typescript.md | ||
|
|
||
| --- | ||
|
|
||
| # LeetCode 102 · Binary Tree Level Order Traversal — TypeScript版 | ||
|
|
||
| --- | ||
|
|
||
| ## 目次(Table of Contents) | ||
|
|
||
| - [1. Overview](#overview) | ||
| - [2. Algorithm](#algorithm) | ||
| - [3. Complexity](#complexity) | ||
| - [4. Implementation](#implementation) | ||
| - [5. Optimization](#optimization) | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="overview">1. Overview</h2> | ||
|
|
||
| > 💡 **この問題は一言で言うと「木を上から下へ、同じ高さのノードをまとめてグループ化する問題」です。** | ||
| > 木の「同じ深さ(階層)」にあるすべての値をひとつの配列にまとめ、その配列を深さ順に並べた2次元配列を返します。 | ||
|
|
||
| ``` | ||
| 3 ← 深さ0: [3] | ||
| / \ | ||
| 9 20 ← 深さ1: [9, 20] | ||
| / \ | ||
| 15 7 ← 深さ2: [15, 7] | ||
|
|
||
| 出力: [[3], [9, 20], [15, 7]] | ||
| ``` | ||
|
|
||
| ### 競技プログラミング・業務開発視点 | ||
|
|
||
| - **ノード数**: 最大 2000 なので O(n) が必須。 | ||
| - **型安全性**: `TreeNode | null` という **Union型** を安全に扱う必要がある。 | ||
| - **JSの特性**: `Array.shift()` は $O(n)$ のため、キューとして使う場合はポインタ管理($O(1)$)を行う必要がある。 | ||
|
|
||
| > 📖 **このセクションで登場した用語** | ||
| > | ||
| > - **Union型**:`A | B` のように「AまたはB」を表す TypeScript の型 | ||
| > - **null安全性**:`null` や `undefined` によるクラッシュをコンパイル時に防ぐ仕組み | ||
| > - **型ガード**:`if (node !== null)` のような条件で、その後のコードブロック内の型を自動的に絞り込む仕組み | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="algorithm">2. Algorithm</h2> | ||
|
|
||
| ### アプローチ比較 | ||
|
|
||
| | アプローチ | 時間計算量 | 空間計算量 | 備考 | | ||
| | -------------------------- | ---------- | ---------- | -------------------------------- | | ||
| | **BFS(幅優先探索)** | O(n) | O(n) | ✅ 今回の選択 | | ||
| | DFS(深さ優先探索) | O(n) | O(n) | 再帰でも実装可能だが直感的でない | | ||
| | 総当たり(各深さでループ) | O(n²) | O(n) | 非推奨 | | ||
|
|
||
| ### BFS(幅優先探索)の仕組み | ||
|
|
||
| BFS は「同じ階のすべての部屋を開けてから、次の階に進むエレベーター」のようなものです。 | ||
| これを実現するのが **キュー(FIFO: First In, First Out)** です。 | ||
|
|
||
| - **核心テクニック**: キューから「今の階のノード数分だけ」取り出すことで、自然に「1階ぶんのグループ」が作れる。 | ||
| - **TypeScript特有の工夫**: キューの型を `TreeNode[]` と明示することで、取り出した要素が必ず `TreeNode` 型になりコンパイル時に安全を保証。 | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="complexity">3. Complexity</h2> | ||
|
|
||
| | 項目 | 値 | 理由 | | ||
| | -------------- | ---- | -------------------------------------------------------------------------------------------- | | ||
| | **時間計算量** | O(n) | 各ノードをキューへの追加・取り出しでちょうど1回ずつ処理するため(ポインタ管理で各操作 O(1)) | | ||
| | **空間計算量** | O(n) | キューに最大で「木の最も広い階のノード数」が入る。最悪ケースは全ノード数 n に比例 | | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="implementation">4. Implementation</h2> | ||
|
|
||
| ### 業務開発版(型安全・パフォーマンス最適化) | ||
|
|
||
| ```typescript | ||
| function levelOrder(root: TreeNode | null): number[][] { | ||
| if (root === null) return []; | ||
|
|
||
| const result: number[][] = []; | ||
| const queue: TreeNode[] = [root]; | ||
| let head = 0; // shift() の O(n) を避けるための先頭ポインタ | ||
|
|
||
| while (head < queue.length) { | ||
| // ── 今この瞬間のキューの長さ = 「現在の階のノード数」 ── | ||
| // 未処理分(queue.length - head)を変数に保存して固定する。 | ||
| const levelSize: number = queue.length - head; | ||
| const levelValues: number[] = []; | ||
|
|
||
| for (let i = 0; i < levelSize; i++) { | ||
| // head インデックスでポインタを進めることで O(1) で取り出す | ||
| const node = queue[head++]!; | ||
| levelValues.push(node.val); | ||
|
|
||
| // 次の階の準備 | ||
| if (node.left !== null) queue.push(node.left); | ||
| if (node.right !== null) queue.push(node.right); | ||
| } | ||
|
|
||
| result.push(levelValues); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| ``` | ||
|
|
||
| ### 🔍 動作トレース(`root = [3, 9, 20, null, null, 15, 7]`) | ||
|
|
||
| ``` | ||
| ━━━━━━━━━━ while ループ 1回目(深さ0) ━━━━━━━━━━ | ||
| levelSize = 1 ← queue.length(1) - head(0) = 1 | ||
| i=0: node = queue[head++] → Node(3) queue = [3], head = 1 | ||
| node.left = 9, node.right = 20 を queue に追加 | ||
| result.push([3]) | ||
|
|
||
| ━━━━━━━━━━ while ループ 2回目(深さ1) ━━━━━━━━━━ | ||
| levelSize = 2 ← queue.length(3) - head(1) = 2 | ||
| i=0, 1: Node(9), Node(20) を処理 | ||
| result.push([9, 20]) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="optimization">5. Optimization</h2> | ||
|
|
||
| ### パフォーマンス最適化:`Array.shift()` の回避 | ||
|
|
||
| JavaScript の `Array.shift()` は、先頭要素を削除した後に残り全要素のインデックスを 1 つずつ前にずらすため、**$O(n)$ の計算量**がかかります。 | ||
| キュー操作を伴う BFS で `shift()` をループ内で使用すると、全体の計算量が **$O(n^2)$** に悪化してしまいます。 | ||
|
|
||
| **解決策:** | ||
| `head` 変数を用いて現在の先頭位置を指し示し、要素を取り出すたびに `head++` する方式を採用します。これにより、実質的な削除操作を伴わずに $O(1)$ で先頭要素を取得できます。 | ||
|
|
||
| > 📖 **このセクションで登場した用語** | ||
| > | ||
| > - **`head` インデックス**:配列の先頭を指すポインタ。 | ||
| > - **FIFO**:First In First Out。先に入れたものを先に取り出す順序。 | ||
| > - **`!`(非null断言)**:TypeScriptに「この値は絶対 null でない」と伝える記号。 |
171 changes: 171 additions & 0 deletions
171
...ree/claude sonnet 4.6 extended/102. Binary Tree Level Order Traversal/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| # Binary Tree Level Order Traversal - 木を階層ごとにグループ化する | ||
|
|
||
| --- | ||
|
|
||
| ## 目次(Table of Contents) | ||
|
|
||
| - [1. Overview](#overview) | ||
| - [2. Algorithm](#algorithm) | ||
| - [3. Complexity](#complexity) | ||
| - [4. Implementation](#implementation) | ||
| - [5. Optimization](#optimization) | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="overview">1. Overview</h2> | ||
|
|
||
| > 💡 **この問題は一言で言うと「木を上から下へ、同じ高さのノードをまとめてグループ化する問題」です。** | ||
|
|
||
| 与えられた二分木(=各ノードが最大2つの子を持つ木構造のデータ)のノードを、 | ||
| **深さ(根からの距離)が同じものをひとつの配列にまとめ**、深さ順に並べた2次元配列を返します。 | ||
|
|
||
| ``` | ||
| 3 ← 深さ0 → [3] | ||
| / \ | ||
| 9 20 ← 深さ1 → [9, 20] | ||
| / \ | ||
| 15 7 ← 深さ2 → [15, 7] | ||
|
|
||
| 出力: [[3], [9, 20], [15, 7]] | ||
| ``` | ||
|
|
||
| **なぜこの問題が難しいのか:** | ||
| 木構造を「縦(深さ方向)」に探索するのは直感的ですが、この問題は「横(同じ深さ)」の単位でまとめる必要があります。 | ||
| そのため、**同じ深さにあるノードをすべて処理し終えてから次の深さへ進む**BFS(幅優先探索)という手法を使う必要があり、 | ||
| その実現に `collections.deque` というデータ構造が鍵を握ります。 | ||
|
|
||
| **制約:** | ||
|
|
||
| | 項目 | 範囲 | | ||
| | ---------- | ------------------------------------------------ | | ||
| | ノード数 | 0 以上 2000 以下 | | ||
| | ノードの値 | -1000 以上 1000 以下(**0 が含まれる点に注意**) | | ||
|
|
||
| > 📖 **この章で登場した用語** | ||
| > | ||
| > - **二分木**:各ノードが左の子・右の子の最大2つを持つ木構造のデータ | ||
| > - **深さ**:根ノードからそのノードまでの辺の数。根の深さは 0 | ||
| > - **BFS(幅優先探索)**:グラフや木を「横方向に広がりながら」探索する方法 | ||
| > - **制約**:入力として与えられる値の範囲や条件のこと | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="algorithm">2. Algorithm</h2> | ||
|
|
||
| > 💡 **TL;DR(Too Long; Didn't Read)** とは「長くて読めない人向けの要約」という意味です。 | ||
| > ここではアルゴリズム全体の戦略をざっくり把握するための章です。 | ||
|
|
||
| - **手法:BFS(幅優先探索)** | ||
| 木を「同じ深さのノードをすべて処理してから次の深さへ進む」順番で探索する。 | ||
| これが今回の「階層ごとのグループ化」と自然に一致するため選ぶ。 | ||
|
|
||
| - **データ構造:`collections.deque`(両端キュー)** | ||
| 先頭からの取り出しが O(1) で行えるため、キュー(行列)として最適。 | ||
| `list.pop(0)` を使うと先頭取り出しが O(n) になり全体が O(n²) へ悪化するため使わない。 | ||
|
|
||
| - **核心テクニック:`level_size = len(queue)` をループ前に固定する** | ||
| ループ中にキューへの追加・取り出しが同時に起きるため、「今の階のサイズ」を先に変数へ保存しないとズレが生じる。 | ||
|
|
||
| ### 図解 | ||
|
|
||
| ```mermaid | ||
| flowchart TD | ||
| Start[Start levelOrder root] --> IsNone{root is None} | ||
| IsNone -- Yes --> RetEmpty[Return empty list] | ||
| IsNone -- No --> Init[Init result and queue with root] | ||
| Init --> WhileLoop{queue is not empty} | ||
| WhileLoop -- No --> RetResult[Return result] | ||
| WhileLoop -- Yes --> FixSize[level_size = len queue] | ||
| FixSize --> InitVals[vals = empty list] | ||
| InitVals --> ForLoop{i less than level_size} | ||
| ForLoop -- No --> AppendLevel[result.append vals] | ||
| AppendLevel --> WhileLoop | ||
| ForLoop -- Yes --> PopNode[node = queue.popleft] | ||
| PopNode --> AppendVal[vals.append node.val] | ||
| AppendVal --> CheckLeft{node.left exists} | ||
| CheckLeft -- Yes --> PushLeft[queue.append node.left] | ||
| CheckLeft -- No --> CheckRight{node.right exists} | ||
| PushLeft --> CheckRight | ||
| CheckRight -- Yes --> PushRight[queue.append node.right] | ||
| CheckRight -- No --> NextI[i plus 1] | ||
| PushRight --> NextI | ||
| NextI --> ForLoop | ||
| ``` | ||
|
|
||
| ### 正しさのスケッチ | ||
|
|
||
| 1. **不変条件**:「while ループの各反復の開始時点で、`queue` には現在の階のノードだけが入っている」 | ||
| - `level_size` 回だけ `popleft()` することで今の階を全部処理し、その間に追加された子は「次の階」として末尾に積まれる。 | ||
| 2. **網羅性**:ルートから始まり、全ノードの左右の子をキューに積むため、存在する全ノードがちょうど1回処理される。 | ||
| 3. **基底条件**:`root is None` なら `[]` を返す。`while queue` で全ノード処理後に終了する。 | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="complexity">3. Complexity</h2> | ||
|
|
||
| | 項目 | 値 | 理由 | | ||
| | -------------- | ---- | ---------------------------------------------------------------------------- | | ||
| | **時間計算量** | O(n) | 各ノードをキューへの追加・取り出しでちょうど1回ずつ処理する(各操作 O(1)) | | ||
| | **空間計算量** | O(n) | `result` 配列と、最大で最下層のノード数(最大 n/2 個)を保持するキューのため | | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="implementation">4. Implementation</h2> | ||
|
|
||
| ### Python 実装(業務開発版) | ||
|
|
||
| ```python | ||
| from collections import deque | ||
| from typing import Optional | ||
|
|
||
| class Solution: | ||
| def levelOrder(self, root: Optional[TreeNode]) -> list[list[int]]: | ||
| if root is None: | ||
| return [] | ||
|
|
||
| result: list[list[int]] = [] | ||
| queue: deque[TreeNode] = deque([root]) | ||
|
|
||
| while queue: | ||
| level_size: int = len(queue) | ||
| level_values: list[int] = [] | ||
|
|
||
| for _ in range(level_size): | ||
| node: TreeNode = queue.popleft() | ||
| level_values.append(node.val) | ||
|
|
||
| if node.left is not None: | ||
| queue.append(node.left) | ||
| if node.right is not None: | ||
| queue.append(node.right) | ||
|
|
||
| result.append(level_values) | ||
|
|
||
| return result | ||
| ``` | ||
|
|
||
| ### エッジケースと検証観点 | ||
|
|
||
| | ケース | 入力 | 期待出力 | 対処箇所 | | ||
| | ------------------ | ------------- | --------------- | ---------------------------- | | ||
| | 空ツリー | `root = None` | `[]` | `if root is None: return []` | | ||
| | `val = 0` のノード | `[0]` | `[[0]]` | `vals.append(node.val)` | | ||
| | 偏った木 (左のみ) | `1→2→3` | `[[1],[2],[3]]` | `level_size` の固定 | | ||
|
|
||
| --- | ||
|
|
||
| <h2 id="optimization">5. Optimization</h2> | ||
|
|
||
| ### CPython 最適化ポイント | ||
|
|
||
| 1. **`list.pop(0)` → `deque.popleft()`**: | ||
| `list.pop(0)` は O(n) の要素シフトが発生し全体で O(n²) になるが、`deque.popleft()` は O(1) で動作する。 | ||
| 2. **`or` トリックの回避**: | ||
| `0 or extend(...)` は `val=0`(falsy)のときに右辺を評価してしまい、戻り値 `None` がリストに混入するため、安全な `append()` を使用する。 | ||
|
|
||
| ### FAQ | ||
|
|
||
| - **Q: なぜ `level_size` を先に保存するのですか?** | ||
| - **A**: ループ内で `append()` が行われるため、`len(queue)` が動的に増えてしまい、次の階のノードまで今の階として処理してしまうのを防ぐためです。 | ||
| - **Q: DFS でも解けますか?** | ||
| - **A**: はい。ただし「今どの深さにいるか」を引数で持ち回る必要があり、この問題には BFS の方が直感的で自然です。 | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.