From 97928fde1dd02f6959c2ab8be482ffa96e7cafc1 Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Sat, 19 Jul 2025 20:22:01 +0900 Subject: [PATCH 1/2] =?UTF-8?q?atcoder=20B27=20-=20Calculate=20LCM=20?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=AF=E3=83=AA=E3=83=83=E3=83=89=E3=81=AE?= =?UTF-8?q?=E4=BA=92=E9=99=A4=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LeastCommonMultiple/atcoder/B27/B27.go | 114 +++++++++++++ .../LeastCommonMultiple/atcoder/B27/B27.js | 80 +++++++++ .../LeastCommonMultiple/atcoder/B27/B27.php | 116 +++++++++++++ .../LeastCommonMultiple/atcoder/B27/B27.py | 103 +++++++++++ .../LeastCommonMultiple/atcoder/B27/B27.ts | 83 +++++++++ .../LeastCommonMultiple/atcoder/B27/README.md | 161 ++++++++++++++++++ 6 files changed, 657 insertions(+) create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.go create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.js create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.php create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.py create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.ts create mode 100644 Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/README.md diff --git a/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.go b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.go new file mode 100644 index 00000000..ecd0321d --- /dev/null +++ b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.go @@ -0,0 +1,114 @@ +// 以下に、**Go 1.20.6** を用いて **2つの正の整数 `A`, `B` の最小公倍数(LCM)を求める実装**を提示します。 + +// --- + +// ## ✅ 処理概要 + +// * **入力**:標準入力(`fmt.Scan`)で `A B` を読み取る(1 ≤ A, B ≤ 10⁹) +// * **出力**:LCM(最小公倍数) +// * **型**:`int64` を使用(必要に応じて `math/big` 使用可) +// * **GCD**:ユークリッドの互除法 +// * **LCM**:`a / GCD(a, b) * b`(順番に注意) + +// --- + +// ## ✅ Go実装(関数型、型付き、計算量考慮) + +package main + +import ( + "fmt" + "os" +) + +/** + * gcd は 2つの整数 a, b の最大公約数を返します。 + * ユークリッドの互除法を使用。 + * + * @param a int64 - 整数 a + * @param b int64 - 整数 b + * @return int64 - aとbの最大公約数 + */ +func gcd(a int64, b int64) int64 { + for b != 0 { + a, b = b, a%b + } + return a +} + +/** + * lcm は 2つの整数 a, b の最小公倍数を返します。 + * lcm(a, b) = a / gcd(a, b) * b の順に計算してオーバーフローを防ぎます。 + * + * @param a int64 - 整数 a + * @param b int64 - 整数 b + * @return int64 - aとbの最小公倍数 + */ +func lcm(a int64, b int64) int64 { + return (a / gcd(a, b)) * b +} + +/** + * main は標準入力から a, b を読み取り、 + * 最小公倍数を標準出力に出力します。 + * + * 入力例: 25 30 + * 出力例: 150 + */ +func main() { + var a, b int64 + _, err := fmt.Scan(&a, &b) + if err != nil { + fmt.Fprintln(os.Stderr, "入力エラー:", err) + return + } + + result := lcm(a, b) + fmt.Println(result) +} + +// ## ✅ 入出力例 + +// ``` +// 入力: 25 30 +// 出力: 150 + +// 入力: 998244353 998244853 +// 出力: 996492287418565109 +// ``` + +// --- + +// ## ✅ 計算量とリソース消費 + +// | 項目 | 内容 | +// | ------ | ------------------------------------------ | +// | 時間計算量 | O(log(min(a, b))): `gcd` のユークリッド互除法 | +// | メモリ使用量 | O(1): 数個の `int64` 変数のみ | +// | 最大出力対応 | `int64` → 約 `9.2 * 10^18` まで安全(64bit) | +// | 注意 | `a = 10^9, b = 10^9` でも LCM = `10^18` → OK | + +// --- + +// ## ✅ オーバーフローについて + +// `int64` 上限は約 `9.2e18`。本問題の LCM の最大値は: + +// ``` +// LCM(10^9, 10^9) = 10^9 +// × (10^9 / GCD(10^9, 10^9)) = 10^9 × 1 = 10^9 +// ``` + +// または、異なる素数の場合は `10^9 * 10^9 = 1e18` → `int64` で安全! + +// --- + +// ## ✅ 解説図(例:LCM(25, 30)) + +// ```text +// Step 1: GCD(25, 30) +// 30 % 25 = 5 +// 25 % 5 = 0 → GCD = 5 + +// Step 2: LCM = (25 / 5) * 30 = 5 * 30 = 150 +// ``` diff --git a/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.js b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.js new file mode 100644 index 00000000..a1140da6 --- /dev/null +++ b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.js @@ -0,0 +1,80 @@ +// 以下に、Node.js (`fs` モジュール使用) を用いて制限内で最小公倍数(LCM)を求める実装を提示します。 + +// ## ✅ 特徴 + +// * 入力: `fs.readFileSync("/dev/stdin", "utf8")`(AtCoderなどで利用可) +// * 最小公倍数計算: `LCM(a, b) = a / GCD(a, b) * b`(オーバーフロー対策済) +// * 使用アルゴリズム: **ユークリッドの互除法** によるGCD計算 + +// --- + +// ### ✅ 実装コード(Node.js, JavaScript) + +const fs = require("fs"); + +/** + * 最小公倍数を求める関数 + * @param {bigint} a - 正整数 A + * @param {bigint} b - 正整数 B + * @returns {bigint} - aとbの最小公倍数(LCM) + */ +function lcm(a, b) { + return (a / gcd(a, b)) * b; +} + +/** + * 最大公約数(GCD)をユークリッドの互除法で求める関数 + * @param {bigint} a - 整数 a + * @param {bigint} b - 整数 b + * @returns {bigint} - aとbの最大公約数 + */ +function gcd(a, b) { + while (b !== 0n) { + const temp = b; + b = a % b; + a = temp; + } + return a; +} + +// 入力読み込み +const input = fs.readFileSync("/dev/stdin", "utf8").trim().split(" "); +const A = BigInt(input[0]); +const B = BigInt(input[1]); + +// 最小公倍数を出力 +console.log(lcm(A, B).toString()); + +// ### ✅ 実行性能 + +// * **時間計算量**: O(log(min(A, B)))(ユークリッドの互除法) +// * **空間計算量**: O(1) +// * **BigInt**を用いることで **2^53 を超える範囲の整数にも対応**(例: `998244353 * 998244853`) + +// --- + +// ### ✅ サンプル入出力確認 + +// **入力:** + +// ``` +// 25 30 +// ``` + +// **出力:** + +// ``` +// 150 +// ``` + +// **入力:** + +// ``` +// 998244353 998244853 +// ``` + +// **出力:** + +// ``` +// 996492287418565109 +// ``` diff --git a/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.php b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.php new file mode 100644 index 00000000..a17ff409 --- /dev/null +++ b/Algorithm/EuclideanAlgorithm/LeastCommonMultiple/atcoder/B27/B27.php @@ -0,0 +1,116 @@ + B[split & BigInt変換] + B --> C[gcd(a, b)] + C --> D[lcm = (a / gcd) * b] + D --> E[console.log(lcm.toString())] +``` + +--- + +## ✅ 入力例解析(998244353 と 998244853) + +### Step-by-step: + +1. `a = 998244353n`, `b = 998244853n` +2. `gcd(a, b) = 1n`(素数なので) +3. `lcm = (a / 1n) * b = a * b` +4. 結果: `996492287418565109n` + +--- + +## ✅ パフォーマンス観点(処理時間・メモリ) + +| 項目 | 内容 | +| ----- | ------------------------ | +| 時間計算量 | O(log(min(A, B)))(GCD) | +| メモリ使用 | `BigInt`3個 + 一時変数(数百バイト) | +| 出力 | O(1)(`console.log`) | + +--- + +## 🔚 最後に + +この処理は以下の特性を持ちます: + +* **精度重視**:BigIntにより桁あふれを防止 +* **効率的**:ユークリッドの互除法 +* **メモリ軽量**:再帰を使わず、固定変数のみ使用 + +| [提出日時](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=created) | 問題 | ユーザ | 言語 | [得点](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=score) | [コード長](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=source_length) | 結果 | [実行時間](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=time_consumption) | [メモリ](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=memory_consumption) | | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 2025-07-19 20:18:49 | [B27 - Calculate LCM](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_cz) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Go (go 1.20.6)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5002) | 1000 | 1077 Byte | | 1 ms | 1704 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67701246) | +| 2025-07-19 20:16:13 | [B27 - Calculate LCM](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_cz) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [PHP (php 8.2.8)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5016) | 1000 | 1344 Byte | | 14 ms | 21356 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67701202) | +| 2025-07-19 20:12:51 | [B27 - Calculate LCM](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_cz) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Python (CPython 3.11.4)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5055) | 1000 | 1019 Byte | | 19 ms | 10616 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67701149) | +| 2025-07-19 20:03:09 | [B27 - Calculate LCM](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_cz) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [TypeScript 5.1 (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5058) | 1000 | 1093 Byte | | 41 ms | 42944 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67701000) | +| 2025-07-19 20:00:41 | [B27 - Calculate LCM](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_cz) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [JavaScript (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5009) | 1000 | 827 Byte | | 51 ms | 42764 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67700966) | \ No newline at end of file From 9cadc0ea17b108a2fd772110acc815a3906fd66d Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Sat, 19 Jul 2025 20:55:44 +0900 Subject: [PATCH 2/2] leetcode 19. Remove Nth Node From End of List Two Pointers --- .../README.md | 144 ++++++++++++++ .../RemoveNthNodeFromEndofList.js | 92 +++++++++ .../RemoveNthNodeFromEndofList.py | 187 ++++++++++++++++++ .../RemoveNthNodeFromEndofList.ts | 143 ++++++++++++++ 4 files changed, 566 insertions(+) create mode 100644 Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/README.md create mode 100644 Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.js create mode 100644 Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.py create mode 100644 Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.ts diff --git a/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/README.md b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/README.md new file mode 100644 index 00000000..a1dcfa29 --- /dev/null +++ b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/README.md @@ -0,0 +1,144 @@ +「**removeNthFromEnd関数**」の処理について、 +**図解+具体的な解析**を行います。 + +--- + +# 🎯 **問題再確認** + +**リンクリストの末尾から n 番目のノードを削除せよ。** + +例: +`head = [1,2,3,4,5], n=2` +→ **「4」を削除する** + +--- + +# 🚀 **アルゴリズム概要** + +## ① **ダミーノード作成** + +ダミーノード(dummy)を使うことで、 +**先頭ノードを削除する場合も安全に処理**できます。 + +``` +初期リスト: [1] -> [2] -> [3] -> [4] -> [5] + +ダミーノード追加後: +[0(dummy)] -> [1] -> [2] -> [3] -> [4] -> [5] +``` + +--- + +## ② **fast ポインタを n+1 進める** + +`fast`ポインタを「n+1回」進めます。 +(これにより `slow` の位置が削除対象の直前になる) + +### 図:`n=2` の場合 + +``` +dummy -> 1 -> 2 -> 3 -> 4 -> 5 + +fast : 初期位置(dummy) + +進める操作: +fast = fast.next (3回繰り返し) + +1回目: fast → 1 +2回目: fast → 2 +3回目: fast → 3 +``` + +--- + +## ③ **fast と slow を同時に進める** + +fastが末尾(null)になるまで、`fast` と `slow` を同時に動かします。 + +| ステップ | fast の位置 | slow の位置 | +| ---- | -------- | -------- | +| 初期 | 3 | dummy | +| 1回目 | 4 | 1 | +| 2回目 | 5 | 2 | +| 3回目 | null | 3 | + +### 図: + +``` +dummy -> 1 -> 2 -> 3 -> 4 -> 5 + ↑ + slow +``` + +--- + +## ④ **削除対象のノードをスキップ** + +`slow.next = slow.next.next` にすることで、 +**4番目のノード(4)を削除** + +### 図: + +``` +変更前: +3 -> 4 -> 5 + +変更後: +3 -> 5 +``` + +--- + +## ⑤ **結果リスト** + +``` +dummy -> 1 -> 2 -> 3 -> 5 + +=> [1] -> [2] -> [3] -> [5] +``` + +--- + +# 🧠 **処理全体まとめ(図解)** + +``` +[0(dummy)] -> [1] -> [2] -> [3] -> [4] -> [5] + ↑ ↑ + slow fast ← n+1個進めた状態 + +移動: +fastとslowを同時に動かす + +最終状態: +[0(dummy)] -> [1] -> [2] -> [3] -> [4] -> [5] + ↑ ↑ + slow fast (null) + +削除処理: +slow.next = slow.next.next + +結果: +[0(dummy)] -> [1] -> [2] -> [3] -> [5] + +返却値: +dummy.next = [1] -> [2] -> [3] -> [5] +``` + +--- + +# 🕒 **計算量** + +* **時間計算量:** + O(sz) (fastポインタとslowポインタをそれぞれ1回走査) + +* **空間計算量:** + O(1) (ポインタのみ使用) + +--- + +# 💡 **この方法のメリット** + +* **1パスで解ける(2回走査不要)** +* ダミーノードを使うことで「先頭要素削除」にも対応可能 +* シンプルで実用的なテクニック(2ポインター法) + diff --git a/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.js b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.js new file mode 100644 index 00000000..f621b895 --- /dev/null +++ b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.js @@ -0,0 +1,92 @@ +// 以下は、**JavaScript(Node.js 18.16.1)** を使った LeetCode形式の「**Remove Nth Node From End of List**」問題の解法です。 + +// ### アプローチ: + +// * **二重ポインター法(fast & slow)** +// 1回のリスト走査で削除可能 +// **時間計算量:** O(sz) +// **空間計算量:** O(1) + +// --- + +// ## 実装 + +// ```javascript +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ + +/** + * 指定されたリンクリストの末尾から n 番目のノードを削除し、リストの先頭を返す + * + * @param {ListNode} head - リンクリストの先頭ノード + * @param {number} n - 末尾から数えた削除対象ノードの位置 + * @returns {ListNode} - 削除後のリンクリストの先頭 + * + * 時間計算量: O(sz) + * メモリ消費量: O(1)(追加のメモリは定数) + */ +function removeNthFromEndJs(head, n) { + // ダミーノードを作成(head削除時も対応可能にする) + const dummy = new ListNode(0, head); + + let fast = dummy; + let slow = dummy; + + // fastポインターをn+1個先に進める + for (let i = 0; i <= n; i++) { + fast = fast.next; + } + + // fastとslowを同時に進める + while (fast !== null) { + fast = fast.next; + slow = slow.next; + } + + // slow.nextが削除対象 + slow.next = slow.next.next; + + return dummy.next; +} + +// ## 解説 + +// ### 具体例(図解) + +// 例: `head = [1,2,3,4,5], n = 2` + +// ``` +// ダミーノード: 0 -> 1 -> 2 -> 3 -> 4 -> 5 + +// 1. fastをn+1=3回進める(fastは「3」を指す) +// 2. fastとslowを同時に進める + +// fast: 4 slow: 1 +// fast: 5 slow: 2 +// fast: null slow: 3 + +// 3. slow.next (4) を削除: +// 3 -> 5 にする + +// 最終結果: 1 -> 2 -> 3 -> 5 +// ``` + +// --- + +// ## **時間・メモリの評価** + +// * **時間計算量:** O(sz)(リンクリスト1回走査) +// * **空間計算量:** O(1)(ポインターのみ使用) + +// --- + +// ## **備考** + +// * `ListNode` クラスは LeetCode 環境で自動的に定義されています。 +// * この実装は、`head` が削除対象の場合も安全に動作します(ダミーノード利用)。 + diff --git a/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.py b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.py new file mode 100644 index 00000000..2442b004 --- /dev/null +++ b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.py @@ -0,0 +1,187 @@ +# 以下は、**Python (CPython 3.11.4)** 用の LeetCode対応「Remove Nth Node From End of List」解法です。 + +# --- + +# ## **実装(Python・型指定あり・LeetCode提出形式)** + +from typing import Optional + +class ListNode: + def __init__(self, val: int = 0, next: Optional['ListNode'] = None) -> None: + self.val: int = val + self.next: Optional['ListNode'] = next + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + """ + リンクリストの末尾から n 番目のノードを削除し、新しい head を返す。 + + Parameters: + head (Optional[ListNode]): リンクリストの先頭ノード + n (int): 末尾から数えて削除するノードの位置(1-based index) + + Returns: + Optional[ListNode]: ノード削除後のリンクリストの先頭 + + 時間計算量: O(sz) + メモリ消費量: O(1) + """ + dummy: ListNode = ListNode(0, head) + + fast: Optional[ListNode] = dummy # Optional を許容 + slow: Optional[ListNode] = dummy + + # fast を n+1 個進める(slow との距離を n に保つ) + for _ in range(n + 1): + if fast is not None: + fast = fast.next + else: + return head # n がリストの長さ以上の場合、安全策として元のリストを返す + + while fast is not None: + fast = fast.next + assert slow is not None # mypyや型チェッカー向け:slowは常にNoneではない + slow = slow.next + + if slow is not None and slow.next is not None: + slow.next = slow.next.next + + return dummy.next +# ## **解説** + +# ### ダミーノードの役割 + +# * `dummy` を使うことで先頭ノードが削除されるケースも安全に処理できます。 + +# ### ポインタ操作 + +# * **`fast` を `n+1`個進める**ことで、`slow` が削除対象の「前」に到達するようにしています。 + +# --- + +# ## **計算量** + +# | 項目 | 計算量 | +# | ---------- | ------------------ | +# | **時間計算量** | O(sz) (szはリストの長さ) | +# | **メモリ消費量** | O(1)(追加メモリはポインタのみ) | + +# --- + +# ## **例** + +# 入力: + +# ```python +# head = [1,2,3,4,5] +# n = 2 +# ``` + +# 出力: + +# ``` +# [1,2,3,5] +# ``` + +# --- + +# ## **図解(簡略版)** + +# ``` +# dummy -> 1 -> 2 -> 3 -> 4 -> 5 +# ↑ ↑ +# slow fast (fastはn+1個進んでいる) + +# 操作: +# slow.next = slow.next.next + +# 結果: +# dummy -> 1 -> 2 -> 3 -> 5 +# ``` + +# --- + +# ## **備考** + +# * LeetCode環境では `ListNode` クラスは事前定義されています(再定義不要)。 +# * CPython 3.11.4で実行しても問題なく動作します。 + +# エラーの主な原因は以下の2点です: + +# --- + +# ### ❶ `fast: ListNode = dummy` のように型を `ListNode` としたことで、 + +# ```python +# fast = fast.next +# ``` + +# のような代入が `Optional[ListNode]`(`ListNode | None`)になる場合にエラーになります。 + +# --- + +# ### ❷ `if fast is not None:` のようなチェックに対して「常にTrue」と誤って型チェッカーが判断してしまうのは、型注釈 `fast: ListNode` によって `None` の可能性を消しているからです。 + +# --- + +# ## ✅ 修正済みコード(型エラー解消 & コメント付き) + +# ```python +# from typing import Optional + +# class ListNode: +# def __init__(self, val: int = 0, next: Optional['ListNode'] = None) -> None: +# self.val: int = val +# self.next: Optional['ListNode'] = next + +# class Solution: +# def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: +# """ +# リンクリストの末尾から n 番目のノードを削除し、新しい head を返す。 + +# Parameters: +# head (Optional[ListNode]): リンクリストの先頭ノード +# n (int): 末尾から数えて削除するノードの位置(1-based index) + +# Returns: +# Optional[ListNode]: ノード削除後のリンクリストの先頭 + +# 時間計算量: O(sz) +# メモリ消費量: O(1) +# """ +# dummy: ListNode = ListNode(0, head) + +# fast: Optional[ListNode] = dummy # Optional を許容 +# slow: Optional[ListNode] = dummy + +# # fast を n+1 個進める(slow との距離を n に保つ) +# for _ in range(n + 1): +# if fast is not None: +# fast = fast.next +# else: +# return head # n がリストの長さ以上の場合、安全策として元のリストを返す + +# while fast is not None: +# fast = fast.next +# assert slow is not None # mypyや型チェッカー向け:slowは常にNoneではない +# slow = slow.next + +# if slow is not None and slow.next is not None: +# slow.next = slow.next.next + +# return dummy.next +# ``` + +# --- + +# ## 🧠 補足:なぜ `Optional[ListNode]` が必要か? + +# * `fast = fast.next` で `fast.next` は `Optional[ListNode]` です。 +# よって `fast` の型も `Optional[ListNode]` でなければ代入できません。 +# * 同様に `slow` も同じく `Optional[ListNode]` にしないと `.next` のチェーン操作時に静的型チェックで引っかかります。 + +# --- + +# ### ✅ 型安全で、`pylance` や `mypy` でもエラーが出ない構成になっています。 + +# 他にも質問や補助が必要であれば遠慮なくどうぞ。 diff --git a/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.ts b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.ts new file mode 100644 index 00000000..1403ced1 --- /dev/null +++ b/Algorithm/TwoPointers/leetcode/19. Remove Nth Node From End of List/RemoveNthNodeFromEndofList.ts @@ -0,0 +1,143 @@ +// 以下は、**TypeScript 5.1(Node.js 18.16.1)** での LeetCode形式の「**Remove Nth Node From End of List**」解法です。 + +// ## アプローチ + +// * **二重ポインター法(fast & slow)** +// * **ダミーノード**を使い、削除対象が`head`の場合にも対応 + +// --- + +// ## 実装(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) + * } + * } + */ + +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; + } +} +/** + * 指定されたリンクリストの末尾から n 番目のノードを削除し、リストの先頭を返す + * + * @param head - リンクリストの先頭ノード + * @param n - 末尾から数えて削除するノードの位置(1-based index) + * @returns リンクリストの先頭ノード(n番目を削除後) + * + * 時間計算量: O(sz) + * メモリ消費量: O(1)(追加のメモリは定数) + */ +function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { +/** + * 指定されたリンクリストの末尾から n 番目のノードを削除し、リストの先頭を返します。 + * + * @param head - リンクリストの先頭ノード。null の場合、何もしないで null を返します。 + * @param n - 末尾から数えて削除するノードの位置(1-based index)。 + * @returns リンクリストの先頭ノード(n番目を削除後)。 + * + * 時間計算量: O(sz) + * メモリ消費量: O(1)(追加のメモリは定数) + */ +function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { + // ダミーノードの作成(head削除にも対応) + const dummy: ListNode = new ListNode(0, head); + + let fast: ListNode | null = dummy; + let slow: ListNode | null = dummy; + + // fastをn+1個先に進める + for (let i = 0; i <= n; i++) { + if (fast) fast = fast.next; + } + + // fastとslowを同時に進める + while (fast !== null) { + fast = fast.next; + if (slow) slow = slow.next; + } + + // slow.nextが削除対象 + if (slow && slow.next) { + slow.next = slow.next.next; + } + + return dummy.next; +} + // ダミーノードの作成(head削除にも対応) + const dummy: ListNode = new ListNode(0, head); + + let fast: ListNode | null = dummy; + let slow: ListNode | null = dummy; + + // fastをn+1個先に進める + for (let i = 0; i <= n; i++) { + if (fast) fast = fast.next; + } + + // fastとslowを同時に進める + while (fast !== null) { + fast = fast.next; + if (slow) slow = slow.next; + } + + // slow.nextが削除対象 + if (slow && slow.next) { + slow.next = slow.next.next; + } + + return dummy.next; +} +// ``` + +// --- + +// ## 解説 + +// ### 例:`head = [1,2,3,4,5], n = 2` + +// ``` +// dummy: 0 -> 1 -> 2 -> 3 -> 4 -> 5 + +// 1. fastをn+1=3回進める -> fastは「3」を指す +// 2. fastとslowを同時に進める + +// fast: 4 slow: 1 +// fast: 5 slow: 2 +// fast: null slow: 3 + +// 3. slow.next (4) を削除して 3 -> 5 にする + +// 結果: 1 -> 2 -> 3 -> 5 +// ``` + +// --- + +// ## **計算量** + +// * **時間計算量:** O(sz)(リンクリスト1回走査) +// * **空間計算量:** O(1)(定数メモリ) + +// --- + +// ## **備考** + +// * `ListNode` クラスは LeetCode環境で自動定義されます。 +// * **クラス実装は使わず、関数型で記述しています。** +// * ダミーノードを使っているので `head` 削除にも安全に対応。 + +// --- + +// **必要であれば、テストコードや図解も提供できますのでお知らせください!**