Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dd83004
feat(leetcode): add Pascal's Triangle II solutions and docs by Claude…
myoshi2891 Jul 6, 2026
823fbd0
feat(leetcode): rename 119 model folder to Claude 5 High and add 125 …
myoshi2891 Jul 6, 2026
10bf816
feat(leetcode): add 136 Single Number solutions and docs by Claude 5
myoshi2891 Jul 6, 2026
fb93e9c
docs(algorithm): add Floyd's Tortoise and Hare solution for LeetCode …
myoshi2891 Aug 4, 2026
5ccc758
docs(math): add Claude Sonnet 5 guide documents for LeetCode 9 Palind…
myoshi2891 Aug 4, 2026
4487c1a
build: auto-generate public directory
myoshi2891 Aug 4, 2026
dafe20d
Merge branch 'main' into dev-from-macmini
myoshi2891 Aug 4, 2026
4010615
Merge branch 'dev-from-macmini' of github.com:myoshi2891/AlgorithmStu…
myoshi2891 Aug 4, 2026
51ae4ad
docs(141-linked-list-cycle): normalize heading levels, fix Floyd cycl…
myoshi2891 Aug 5, 2026
3ee6d9a
docs(119-pascals-triangle-ii): refine itertools.accumulate performanc…
myoshi2891 Aug 5, 2026
5ebe7fa
docs(125-valid-palindrome): clarify SkipNonAlnum flow diagram, align …
myoshi2891 Aug 5, 2026
1aa4eae
docs(136-single-number): update legend color descriptions, add elemen…
myoshi2891 Aug 5, 2026
a9c89b4
docs(9-palindrome-number): remove unsubstantiated speed claims, rejec…
myoshi2891 Aug 5, 2026
30a9c81
docs(algorithm): normalize headings in Linked List Cycle Python guide
myoshi2891 Aug 5, 2026
e035e36
fix(algorithm): update mermaid script tag to local vendor path
myoshi2891 Aug 5, 2026
bdbfadd
build(index): add vendor mapping for mermaid asset replacement
myoshi2891 Aug 5, 2026
4267a01
build: auto-generate public directory
myoshi2891 Aug 5, 2026
3065cc9
docs(13-roman-to-integer): add Sonnet 5 solution documentation and in…
myoshi2891 Aug 6, 2026
e5ede53
docs(algorithm): update header hierarchy and single-node cycle edge c…
myoshi2891 Aug 6, 2026
39d1e45
fix(math): update Mermaid flowchart Compare node labels and CheckEmpt…
myoshi2891 Aug 6, 2026
ce7f0db
Merge branch 'dev-from-macmini' of github.com:myoshi2891/AlgorithmStu…
myoshi2891 Aug 6, 2026
347fbf8
build: auto-generate public directory
myoshi2891 Aug 6, 2026
87ea651
fix(math/roman): fix mermaid linkStyle colors in README_react.html
myoshi2891 Aug 6, 2026
c5183c2
fix(math/roman): update flowchart validation and exception paths in R…
myoshi2891 Aug 6, 2026
21695c1
docs(math/roman): adjust chapter heading levels to H2 in Python and T…
myoshi2891 Aug 6, 2026
226ebf9
Merge branch 'dev-from-macmini' of github.com:myoshi2891/AlgorithmStu…
myoshi2891 Aug 6, 2026
00c3b74
build: auto-generate public directory
myoshi2891 Aug 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# LeetCode: Linked List Cycle(連結リストの循環検出)

---

## 1. 問題の分析

> 💡 **初学者向け補足**:この問題は、一言で言うと「連結リスト(データが数珠つなぎになった構造)をたどっていったときに、同じ場所をぐるぐる回り続けてしまう輪っか(サイクル)があるかどうかを調べる問題」です。

- **競技プログラミング視点での分析**
- 実行速度を最優先する場合、リストの全ノードを最大1〜2回訪問するだけで判定できるアルゴリズムが理想です。
- メモリ使用量の最小化を狙うなら、ノードを記録するための追加データ構造(配列やSet)を一切使わず、ポインタ(=ノードを指し示す変数)だけで判定する方法が最良です。
- **業務開発視点での分析**
- 型安全性・保守性・可読性を重視する場合、「ノードが存在するか」「次のノードが存在するか」を毎回明示的にチェックし、`null`による予期しないクラッシュを防ぐコードにする必要があります。
- `head`が最初から`null`(=空のリスト)の場合など、境界条件(=入力の端っこのパターン)への対応も重要です。
- **TypeScript特有の考慮点**
- `ListNode | null`という「ユニオン型(=複数の型のどれかを取りうるという意味の型)」を正しく扱うために、型ガード(=実行時に型を確認する処理)を活用します。
- ジェネリクスは今回のノードの型が固定(`ListNode`)されているため使用しませんが、型推論(=型を書かなくてもTypeScriptが自動で判断してくれる機能)を活かして無駄な型注釈を減らします。

> 📖 **このセクションで登場した用語**
>
> - **連結リスト(Linked List)**:データ(ノード)が「次はこれだよ」という矢印(ポインタ)でつながった構造。配列と違い、途中への挿入・削除が高速。
> - **サイクル(循環)**:リストをたどっていくと、以前に訪れたノードに戻ってきてしまう輪っか状の構造。
> - **ユニオン型**:「AまたはB」のように、複数の型のうちどれかであることを表すTypeScriptの型。例:`ListNode | null`は「ListNode型か、null」のどちらか。
> - **型ガード**:実行時に「この変数は本当にこの型か?」を確認するためのコード(例:`if (x !== null)`)。

---

## 2. アルゴリズムアプローチ比較

> 💡 **初学者向け補足**:同じ問題でも解き方は複数あります。それぞれの「速さ(時間計算量)」と「メモリの使いやすさ(空間計算量)」を比べて最適なものを選びます。

| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |
| ------------------------------------------------- | ---------- | ---------- | ------------ | -------- | ------ | -------------------------------------- |
| Floyd's Tortoise and Hare(高速・低速ポインタ法) | O(n) | O(1) | 低 | 高 | 高 | 追加メモリ不要、最も推奨される定番解法 |
| ハッシュセット(訪問済みノード記録法) | O(n) | O(n) | 低 | 高 | 高 | 実装は直感的だが、メモリを多く使う |
| ノード改変法(訪問済みフラグを値に埋め込む) | O(n) | O(1) | 中 | 低 | 低 | 入力データを破壊するため実務では非推奨 |

> 💡 **Big-O記法の読み方**(初学者向け)
>
> - `O(1)`:入力の大きさに関わらず、常に一定の時間・メモリで済む(最速・最小)
> - `O(n)`:入力が2倍になると、処理も約2倍になる(線形)

> 📖 **このセクションで登場した用語**
>
> - **時間計算量**:入力の大きさ(ここではノードの数)に対して、処理にかかる手間がどう増えるかの目安。
> - **空間計算量**:処理中に使うメモリ量が、入力の大きさに対してどう増えるかの目安。
> - **ハッシュセット(Set)**:値の「重複なしの集まり」を管理するデータ構造。「すでに見たかどうか」を高速に調べられる。

---

## 3. 選択したアルゴリズムと理由

> 💡 **初学者向け補足**:ハッシュセット法は実装が簡単で理解しやすいですが、ノード数が多いとメモリを大量に消費してしまうため選びませんでした。ノード改変法は入力のデータそのものを書き換えてしまい、他の処理に悪影響を与える可能性があるため選びませんでした。

- **選択したアプローチ**: Floyd's Tortoise and Hare(高速・低速ポインタ法)
- **理由**:
- **計算量的な優位性**: 時間計算量O(n)・空間計算量O(1)と、両方の面で最も効率的です。ノードを2つのポインタ(「遅い亀」と「速いウサギ」に例えられる)だけで管理するため、追加のメモリが一切不要です。
- **TypeScript環境での型安全性**: `ListNode | null`という型を正しく扱うための型ガードを丁寧に書くだけで実装でき、複雑な型定義は不要です。
- **保守性・可読性の観点**: アルゴリズム自体がシンプルなループ構造のみで表現できるため、コードの見通しが良く、他の開発者にも理解しやすいです。
- **TypeScript特有の最適化ポイント**:
- コンパイル時の型チェックにより、「`next`が`null`かもしれないのにアクセスしてしまう」といったバグを未然に防げます。
- 今回はジェネリクスは不要ですが、`readonly`な引数として扱うことで「関数内で誤ってリストを書き換えてしまう」事故を防ぎます(※ただし今回はポインタの移動のみで値の書き換えは発生しません)。

> 📖 **このセクションで登場した用語**
>
> - **ポインタ**:ある変数が「別のデータ(ここではノード)を指し示している」状態、またはその変数自体のこと。
> - **型チェック**:TypeScriptがコードをコンパイル(変換)する際に「型が正しく使われているか」を確認する処理。

---

## 4. 実装コード

> 💡 **初学者向け補足**:コード全体を示す前に、このコードの大まかな構造(骨格)を示します。
>
> 1. まず、`head`が`null`(空リスト)または`head.next`が`null`(次のノードが存在しない)の場合は`false`を返す(※単一ノードでも自身を指すサイクルは作れるが、`head.next`が`null`ならサイクルは存在しない)
> 2. 「遅いポインタ(slow)」と「速いポインタ(fast)」という2つの変数を用意し、両方とも`head`からスタートする
> 3. ループの中で、`slow`は1つずつ、`fast`は2つずつノードを進める
> 4. もし`slow`と`fast`が同じノードを指す瞬間が来たら、それは輪っかの中をぐるぐる回っている証拠なので`true`を返す
> 5. `fast`がリストの終端(`null`)に到達したら、輪っかは存在しないので`false`を返す

```typescript
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/

/**
* 連結リストにサイクル(循環)が存在するかどうかを判定する
* Floyd's Tortoise and Hare(亀と兎のアルゴリズム)を使用する
*
* なぜこの方法が有効なのか:
* サイクルが存在する場合、速いポインタ(兎)は遅いポインタ(亀)よりも
* 2倍速く進むため、輪っかの中で必ずいつか追いつく(=同じノードを指す)瞬間が来る。
* これは、円形のトラックを走る2人のランナーが、速度差がある限り
* 必ずどこかですれ違う(追いつく)のと同じ原理。
*
* @param head - 連結リストの先頭ノード(存在しない場合はnull)
* @returns サイクルが存在すればtrue、存在しなければfalse
* @complexity Time: O(n), Space: O(1)
*/
function hasCycle(head: ListNode | null): boolean {
// 型ガード:リストが空、またはノードが1つしかない場合、
// 「次に進む」動作自体ができないためサイクルは作りようがない
if (head === null || head.next === null) {
return false;
}

// 「遅いポインタ(亀)」:1歩ずつ進む
// 「速いポインタ(兎)」:2歩ずつ進む
// どちらもTypeScript上は ListNode | null 型として扱う(nullになる可能性があるため)
let slow: ListNode | null = head;
let fast: ListNode | null = head;

// fastとfast.nextの両方がnullでない間だけループを続ける
// (fastが2歩ずつ進むため、fast.nextが先にnullになるかもしれないことを考慮)
while (fast !== null && fast.next !== null) {
// 遅いポインタを1歩進める
slow = slow!.next;

// 速いポインタを2歩進める
fast = fast.next.next;

// もし遅いポインタと速いポインタが同じノードを指していたら、
// それはサイクルの中で追いついたということなのでtrueを返す
if (slow === fast) {
return true;
}
}

// ループを抜けた(=fastがリストの終端に到達した)ということは、
// 輪っかがなく、リストがどこかで途切れていたということ
return false;
}
```

> 💡 **コードの動作トレース**(初学者向け)
>
> ```
> 例)入力: head = [3, 2, 0, -4]、pos = 1(末尾の -4 が 2番目のノード「2」に戻る)
>
> ノードの並び: idx0: 3 → idx1: 2 → idx2: 0 → idx3: -4 → (idx1 の「2」に戻る)
>
> 初期状態: slow = 3 (idx0), fast = 3 (idx0)
>
> Step 1: slow = slow.next = 2 (idx1)
> fast = fast.next.next = 0 (idx2)
> slow(2) === fast(0)? → いいえ、続行
>
> Step 2: slow = slow.next = 0 (idx2)
> fast = fast.next.next = 2 (idx1) (idx2 から idx3(-4) を経て idx1(2) へ進む)
> slow(0) === fast(2)? → いいえ、続行
>
> Step 3: slow = slow.next = -4 (idx3)
> fast = fast.next.next = -4 (idx3) (値 2(idx1) から fast.next.next により 0(idx2) → -4(idx3) へ2歩進む)
> slow(-4) === fast(-4)? → はい!同じ ListNode オブジェクトを指している
>
> → true を返す(サイクルが検出された)
> ```

> 📖 **このセクションで登場した用語**
>
> - **Floyd's Tortoise and Hare(亀と兎のアルゴリズム)**:2つのポインタを異なる速度で進めることでサイクルを検出する古典的な手法。
> - **`!`(非nullアサーション演算子)**:TypeScriptに対して「ここは絶対に`null`ではないから安心して」と明示的に伝える記法。今回は直前の`while`条件で`fast !== null`(つまりループの1歩目では`slow`も`fast`も`head`であり非null)が保証されているため使用しています。ただし乱用すると型安全性が失われるため、根拠がある場合のみ使うべきです。
> - **`ListNode | null`**:ノードが存在する場合と、リストの終端(存在しない場合)の両方を表現するための型。

---

## TypeScript固有の最適化観点

### 型安全性の活用

1. **コンパイル時エラー防止**
- `head: ListNode | null`という型により、呼び出し側が誤って`undefined`など不正な値を渡した場合にコンパイル段階で警告できます。
- `slow`と`fast`を`ListNode | null`型として宣言することで、「`.next`にアクセスする前に`null`チェックを忘れる」というミスをコンパイラが指摘してくれます。

2. **null安全性の確保**
- `while (fast !== null && fast.next !== null)`という条件により、`fast.next.next`にアクセスする前に必ず安全性を確認しています。

### 開発効率と保守性

- 型定義(`ListNode | null`)自体が「このノードは存在しないかもしれない」というドキュメントの役割を果たしており、コードを読むだけで境界条件への配慮が伝わります。
- ループ構造がシンプルなため、将来的に「サイクルの開始ノードを求める」(LeetCode 142番の発展問題)といった機能追加も同じ型定義のまま自然に拡張できます。
Loading