Skip to content

Commit a6d4480

Browse files
committed
leetcode 27. Remove Element Two Pointers & Rolling Hash
1 parent 2929ca6 commit a6d4480

7 files changed

Lines changed: 983 additions & 0 deletions

File tree

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
では、Python 解法における `removeElement` メソッドの**各処理を図解付きで詳細に解析・説明**します。処理の流れと状態遷移を**イテレーションごとに可視化**していきます。
2+
3+
---
4+
5+
## 🎯 問題要点の再確認
6+
7+
* `nums` 配列から、指定された `val` を削除(in-place)
8+
* 残った要素は `nums`**先頭 k 個に前詰め**
9+
* **順番は問わない**
10+
* 新しい長さ `k` を返す
11+
12+
---
13+
14+
## 🧠 アルゴリズム概要(Two Pointers)
15+
16+
```python
17+
for i in range(len(nums)):
18+
if nums[i] != val:
19+
nums[k] = nums[i]
20+
k += 1
21+
```
22+
23+
### ✔ ポインタの意味
24+
25+
| ポインタ名 | 意味 |
26+
| ----- | --------------------- |
27+
| `i` | 現在走査しているインデックス |
28+
| `k` | 次に val 以外の要素を書き込む先頭位置 |
29+
30+
---
31+
32+
## 🔎 処理詳細の図解(例: `nums = [0,1,2,2,3,0,4,2]`, `val = 2`
33+
34+
### 初期状態:
35+
36+
```text
37+
nums = [0, 1, 2, 2, 3, 0, 4, 2]
38+
val = 2
39+
k = 0
40+
```
41+
42+
---
43+
44+
### ✅ i = 0: nums\[0] = 0 (val ではない)
45+
46+
```text
47+
nums[0] = 0 → valではないので、nums[k] = nums[0] を実行
48+
49+
k = 0 → nums[0] に 0 を書き込む(同じ値なので実質変化なし)
50+
k を 1 に更新
51+
```
52+
53+
📦 配列状態:
54+
55+
```text
56+
i=0 ↓
57+
[0, 1, 2, 2, 3, 0, 4, 2]
58+
^
59+
k=1
60+
```
61+
62+
---
63+
64+
### ✅ i = 1: nums\[1] = 1 (val ではない)
65+
66+
```text
67+
nums[1] = 1 → valではないので、nums[k] = nums[1]
68+
69+
k = 1 → nums[1] に 1 を書き込む(同じ値なので変化なし)
70+
k を 2 に更新
71+
```
72+
73+
📦 配列状態:
74+
75+
```text
76+
i=1 ↓
77+
[0, 1, 2, 2, 3, 0, 4, 2]
78+
^
79+
k=2
80+
```
81+
82+
---
83+
84+
### ❌ i = 2: nums\[2] = 2 (valと一致)
85+
86+
```text
87+
スキップ。k は変更なし。
88+
```
89+
90+
📦 配列状態:
91+
92+
```text
93+
i=2 ↓
94+
[0, 1, 2, 2, 3, 0, 4, 2]
95+
^
96+
k=2
97+
```
98+
99+
---
100+
101+
### ❌ i = 3: nums\[3] = 2 (valと一致)
102+
103+
```text
104+
スキップ。k は変更なし。
105+
```
106+
107+
📦 配列状態:
108+
109+
```text
110+
i=3 ↓
111+
[0, 1, 2, 2, 3, 0, 4, 2]
112+
^
113+
k=2
114+
```
115+
116+
---
117+
118+
### ✅ i = 4: nums\[4] = 3 (val ではない)
119+
120+
```text
121+
nums[k] = nums[4] → nums[2] に 3 を上書き
122+
k を 3 に更新
123+
```
124+
125+
📦 配列状態:
126+
127+
```text
128+
i=4 ↓
129+
[0, 1, 3, 2, 3, 0, 4, 2]
130+
^
131+
k=3
132+
```
133+
134+
---
135+
136+
### ✅ i = 5: nums\[5] = 0 (val ではない)
137+
138+
```text
139+
nums[k] = nums[5] → nums[3] に 0 を上書き
140+
k を 4 に更新
141+
```
142+
143+
📦 配列状態:
144+
145+
```text
146+
i=5 ↓
147+
[0, 1, 3, 0, 3, 0, 4, 2]
148+
^
149+
k=4
150+
```
151+
152+
---
153+
154+
### ✅ i = 6: nums\[6] = 4 (val ではない)
155+
156+
```text
157+
nums[k] = nums[6] → nums[4] に 4 を上書き
158+
k を 5 に更新
159+
```
160+
161+
📦 配列状態:
162+
163+
```text
164+
i=6 ↓
165+
[0, 1, 3, 0, 4, 0, 4, 2]
166+
^
167+
k=5
168+
```
169+
170+
---
171+
172+
### ❌ i = 7: nums\[7] = 2 (valと一致)
173+
174+
```text
175+
スキップ
176+
```
177+
178+
📦 最終状態:
179+
180+
```text
181+
i=7 ↓
182+
[0, 1, 3, 0, 4, 0, 4, 2]
183+
^
184+
k=5
185+
```
186+
187+
---
188+
189+
## ✅ 最終結果
190+
191+
* 返却値 `k = 5`
192+
* 有効な先頭要素: `nums[:k] = [0, 1, 3, 0, 4]`(順不同でOK)
193+
* `nums[k:]` の中身はどうでもよい(不要領域)
194+
195+
---
196+
197+
## 📊 計算量まとめ
198+
199+
| 指標 ||
200+
| ------- | -------------------- |
201+
| 時間計算量 | O(n) |
202+
| 空間計算量 | O(1) (インプレース処理) |
203+
| 最大配列サイズ | 100 (制約による) |
204+
| メモリ使用量 | 参照配列 `nums` のみ(追加なし) |
205+
206+
---
207+
208+
## 🧪 LeetCode Judge との照合コード
209+
210+
```python
211+
expectedNums = [0, 1, 3, 0, 4]
212+
k = solution.removeElement(nums, val)
213+
assert k == len(expectedNums)
214+
assert sorted(nums[:k]) == sorted(expectedNums)
215+
```
216+
217+
---
218+
219+
以上が `removeElement` 処理の**各ステップを図で示した詳細な解析**になります。
220+
221+
必要に応じて、**処理中のメモリアドレスの可視化**や、**再帰版の比較**なども対応可能です。お気軽にどうぞ。
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// 以下は、LeetCodeの仕様に従って `val` を取り除き、配列 `nums` をインプレース(in-place)で変更し、変更後の長さ `k` を返すJavaScript関数です。クラスを使用せず、関数として実装しています。
2+
3+
// ```javascript
4+
/**
5+
* 配列 nums から val のすべての出現をインプレースで削除し、
6+
* val 以外の要素数 k を返します。
7+
*
8+
* @param {number[]} nums - 入力配列(参照渡しで変更される)
9+
* @param {number} val - 削除する値
10+
* @returns {number} - val 以外の要素の個数(k)
11+
*
12+
* 時間計算量: O(n) — 配列を一度走査する
13+
* 空間計算量: O(1) — 追加メモリは使用しない
14+
*/
15+
function removeElementJs(nums, val) {
16+
let k = 0; // val 以外の値を書き込む位置
17+
18+
for (let i = 0; i < nums.length; i++) {
19+
if (nums[i] !== val) {
20+
nums[k] = nums[i]; // val でない場合、前方に詰める
21+
k++;
22+
}
23+
}
24+
25+
// nums の k 以降の値は問題により重要ではない(そのままでOK)
26+
return k;
27+
}
28+
// ```
29+
30+
// ---
31+
32+
// ### ✅ 使用例(LeetCode Judge テストケースに相当)
33+
34+
// ```javascript
35+
// const nums = [0,1,2,2,3,0,4,2];
36+
// const val = 2;
37+
38+
// const k = removeElement(nums, val);
39+
// console.log("k:", k); // 出力: 5
40+
// console.log("modified nums (first k elements):", nums.slice(0, k));
41+
// 出力例: [0,1,3,0,4] のような順不同の val ≠ 2 の要素たち
42+
// ```
43+
44+
// ---
45+
46+
// ### ✅ 解説と図解
47+
48+
// #### 入力:
49+
50+
// ```
51+
// nums = [0,1,2,2,3,0,4,2]
52+
// val = 2
53+
// ```
54+
55+
// #### ループ処理:
56+
57+
// | i | nums\[i] | nums (途中経過) | k |
58+
// | - | -------- | ---------------- | - |
59+
// | 0 | 0 | \[0,...] | 1 |
60+
// | 1 | 1 | \[0,1,...] | 2 |
61+
// | 2 | 2 | (スキップ) | 2 |
62+
// | 3 | 2 | (スキップ) | 2 |
63+
// | 4 | 3 | \[0,1,3,...] | 3 |
64+
// | 5 | 0 | \[0,1,3,0,...] | 4 |
65+
// | 6 | 4 | \[0,1,3,0,4,...] | 5 |
66+
// | 7 | 2 | (スキップ) | 5 |
67+
68+
// ---
69+
70+
// ### ✅ 処理性能(最大100要素において)
71+
72+
// * **時間計算量:** O(n) (最大で100回ループ)
73+
// * **空間計算量:** O(1) (インプレースで処理、追加メモリなし)
74+
// * **メモリ使用:** 元の配列以外に追加配列を使わず、省メモリ
75+
76+
// ---
77+
78+
// 必要であれば、以下のように要素の先頭 `k` 件をソートして `expectedNums` と比較できます:
79+
80+
// ```javascript
81+
// nums.sort((a, b) => a - b); // 全体ソート
82+
// console.log(nums.slice(0, k)); // => expectedNums と比較対象に
83+
// ```
84+
85+
// 他に図やパフォーマンス検証が必要でしたら、お知らせください。

0 commit comments

Comments
 (0)