Skip to content

Commit 3285bec

Browse files
committed
Refine LeetCode 88 implementations: Update complexity notes, Rust index safety, TS non-null assertions, and React UI pointer rendering
1 parent deeaaab commit 3285bec

6 files changed

Lines changed: 99 additions & 106 deletions

File tree

Algorithm/Sort/MergeSort/leetcode/claude 4.6 sonnet extended/88. Merge Sorted Array/Merge_Sorted_Array_Python.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ LeetCode のシグネチャは `None` 返却の破壊的操作。Pylance 対応
3131

3232
- **選択**: 後ろからの3ポインタマージ(業務版・競技版ともに同一戦略)
3333
- **Python最適化戦略**: 残余コピーをスライス代入 `nums1[:j+1] = nums2[:j+1]` に置換することで、CPython 内部の `memmove` が発動し逐次代入より高速
34-
- **トレードオフ**: スライス代入は一時オブジェクトを1個生成するが定数サイズであり実質 O(1) の追加コスト
34+
- **トレードオフ**: スライス代入は最大 O(n) サイズの一時リストオブジェクトを生成するため、厳密な O(1) 空間計算量が必要な場合は while ループで要素ごとにコピーする手法が適している
3535

3636
---
3737

@@ -60,7 +60,7 @@ class Solution:
6060
n: nums2 の要素数
6161
6262
Time Complexity: O(m + n)
63-
Space Complexity: O(1) ※スライス代入の一時オブジェクト除く
63+
Space Complexity: O(n) ※スライス代入の一時オブジェクトによる最悪ケース
6464
"""
6565
# ── 業務開発版 ─────────────────────────────────────────────
6666
# n == 0 のとき nums1 は既に完成しているため早期リターン

Algorithm/Sort/MergeSort/leetcode/claude 4.6 sonnet extended/88. Merge Sorted Array/Merge_Sorted_Array_Rust.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
**業務開発視点での分析**
88

9-
LeetCode の関数シグネチャは `&mut Vec<i32>` の受け渡しを強制しますが、内部では `usize` インデックスと `saturating_sub` を活用し、符号なし整数のアンダーフローをコンパイル時に潰します`i32 → usize` の変換は `as usize` で確実に行います。
9+
LeetCode の関数シグネチャは `&mut Vec<i32>` の受け渡しを強制しますが、内部では `while i > 0 && j > 0` のような実行時のガード条件と構造的な順序付けにより、符号なし整数のアンダーフローを防止します`i32 → usize` の変換は `as usize` で確実に行います。
1010

1111
**Rust特有の考慮点**
1212

1313
- `i32` 引数 `m`, `n` は LeetCode の制約上 `0..=200` に収まるため、`as usize` キャストは安全
14-
- `checked_sub``saturating_sub` でインデックス計算のパニックを防止
14+
- ランタイムのガードループ(`while i > 0 && j > 0``while j > 0`)によりインデックス計算のパニックを防止
1515
- 借用は `&mut Vec<i32>` のスライス操作に集約し、ライフタイムを単純化
1616

1717
---

Algorithm/Sort/MergeSort/leetcode/claude 4.6 sonnet extended/88. Merge Sorted Array/Merge_Sorted_Array_TypeScript.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ function merge(nums1: number[], m: number, nums2: number[], n: number): void {
6565

6666
// どちらかの配列が空になるまで大きい方を末尾から書き込む
6767
while (i >= 0 && j >= 0) {
68-
if (nums1[i] >= nums2[j]) {
69-
nums1[k] = nums1[i];
68+
if (nums1[i]! >= nums2[j]!) {
69+
nums1[k] = nums1[i]!;
7070
i--;
7171
} else {
72-
nums1[k] = nums2[j];
72+
nums1[k] = nums2[j]!;
7373
j--;
7474
}
7575
k--;
@@ -95,7 +95,7 @@ function merge(nums1: number[], m: number, nums2: number[], n: number): void {
9595

9696
**型安全インデックス管理**
9797

98-
`i >= 0` / `j >= 0` のガードにより、TypeScript の `strict: true` 環境でも `number` 型のインデックスが負にならないことが実行フロー上保証されます。`noUncheckedIndexedAccess` を有効にした場合でも、ガード後の `nums1[i]``number` として安全にアクセスできます
98+
`i >= 0` / `j >= 0` のガードにより、TypeScript の `strict: true` 環境でも `number` 型のインデックスが負にならないことが実行フロー上保証されます。ただし `noUncheckedIndexedAccess` を有効にした場合、ガードだけでは `number | undefined` のままになるため、`nums1[i]!` のように非 null アサーションを用いるか、`if (i in nums1)` 等で存在チェックを行い型を明示的に狭める必要があります
9999

100100
**`while` vs イテレータ**
101101

Algorithm/Sort/MergeSort/leetcode/claude 4.6 sonnet extended/88. Merge Sorted Array/README.md

Lines changed: 75 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,15 @@
44

55
## 目次(Table of Contents)
66

7-
- [概要](#overview)
8-
- [アルゴリズム要点 TL;DR](#tldr)
9-
- [図解](#figures)
10-
- [正しさのスケッチ](#correctness)
11-
- [計算量](#complexity)
12-
- [Python 実装](#impl)
13-
- [CPython 最適化ポイント](#cpython)
14-
- [エッジケースと検証観点](#edgecases)
15-
- [FAQ](#faq)
7+
- [Overview](#overview)
8+
- [Algorithm](#algorithm)
9+
- [Complexity](#complexity)
10+
- [Implementation](#implementation)
11+
- [Optimization](#optimization)
1612

1713
---
1814

19-
<h2 id="overview">概要</h2>
15+
## Overview
2016

2117
### 問題要約
2218

@@ -37,22 +33,21 @@
3733

3834
---
3935

40-
<h2 id="tldr">アルゴリズム要点 TL;DR</h2>
36+
## Algorithm
37+
38+
### アルゴリズム要点 TL;DR
4139

4240
- **戦略**: `nums1` の末尾から走査する **3 ポインタ法**(後ろからマージ)
4341
- **データ構造**: 追加配列不要。`nums1` 自体をバッファとして再利用
4442
- **時間計算量**: O(m + n) — 各要素をちょうど 1 回参照・書き込み
45-
- **空間計算量**: O(1) — 追加ヒープアロケーションなし
46-
(残余コピーのスライス代入は定数サイズの一時オブジェクト 1 個のみ)
43+
- **空間計算量**: O(n) — スライス代入による一時オブジェクト生成の最悪ケース
4744
- **核心の不変条件**: 書き込みポインタ `k` は常に読み取りポインタ `i` 以上
4845
→ 未処理の `nums1` 要素を上書きしない
4946
- **後ろから書く理由**: 前から比較すると `nums1` の有効要素を上書きしてしまうため逆順が安全
5047

51-
---
52-
53-
<h2 id="figures">図解</h2>
48+
### 図解
5449

55-
### フローチャート
50+
#### フローチャート
5651

5752
```mermaid
5853
flowchart TD
@@ -75,9 +70,7 @@ flowchart TD
7570

7671
> 後ろから比較して大きい方を末尾(`k`)に書き込む。どちらかが尽きたら `nums2` の残余を先頭スライスへ一括コピー。
7772
78-
---
79-
80-
### データフロー図(ステップ別状態遷移)
73+
#### データフロー図(ステップ別状態遷移)
8174

8275
```mermaid
8376
graph LR
@@ -113,11 +106,9 @@ graph LR
113106

114107
> 各ステップで大きい方の値が `k` の位置に書き込まれ、対応するポインタと `k` が 1 ずつ減少する。
115108
116-
---
109+
### 正しさのスケッチ
117110

118-
<h2 id="correctness">正しさのスケッチ</h2>
119-
120-
### 不変条件
111+
#### 不変条件
121112

122113
書き込みポインタ `k``nums1` の読み取りポインタ `i` について、以下が常に成立する:
123114

@@ -130,44 +121,46 @@ k - i = (m + n - 1 - step_count) - (m - 1 - i_decrements)
130121
`i` が減少するとき `k - i` は変わらず、`j` が減少するとき `k - i` は 1 増加する。
131122
よって `k ≥ i` が保持され、**未処理の `nums1` 要素を上書きしない**
132123

133-
### 網羅性
124+
#### 網羅性
134125

135126
- `while` ループ終了後、`i < 0` または `j < 0` のどちらかが成立
136127
- `j < 0` の場合:`nums2` は全て書き込み済み、`nums1` の残余は正しい位置にある(移動不要)
137128
- `j ≥ 0` の場合:`nums2[:j+1]``nums1[:j+1]` にスライス代入して完了
138129

139-
### 基底条件(エッジケース)
130+
#### 基底条件(エッジケース)
140131

141132
| 条件 | 処理 |
142133
| -------- | --------------------------------------------- |
143134
| `n == 0` | 早期リターン、`nums1` はそのまま |
144135
| `m == 0` | `while``i >= 0` が即 False → 残余コピーへ |
145136

146-
### 終了性
137+
#### 終了性
147138

148139
`i``j` はループごとに必ず 1 以上減少し、非負整数であるため有限ステップで終了する。
149140

150141
---
151142

152-
<h2 id="complexity">計算量</h2>
143+
## Complexity
153144

154145
| 観点 | 計算量 | 説明 |
155146
| ---------- | -------- | ------------------------------------------ |
156147
| 時間計算量 | O(m + n) | 各要素を最大 1 回比較・書き込み |
157-
| 空間計算量 | O(1) | スライス代入の一時オブジェクトは定数サイズ |
148+
| 空間計算量 | O(n) | スライス代入による一時オブジェクト生成による最悪ケース |
158149

159150
### In-place vs Pure 比較
160151

161152
| 実装方針 | 時間 | 空間 | 備考 |
162153
| ----------------------------- | ----------------- | -------- | --------------------------------------- |
163-
| 本実装(後ろから 3 ポインタ) | O(m+n) | **O(1)** | ✅ 最適 |
154+
| 本実装(後ろから 3 ポインタ) | O(m+n) | **O(n)** | スライス代入の空間コストを含む |
164155
| 前から + 一時バッファ | O(m+n) | O(m) | `nums1[:m]``list()` でコピー |
165156
| `nums1 + nums2``.sort()` | O((m+n) log(m+n)) | O(1) | Timsort は C 実装で高速だが計算量は劣る |
166157
| `heapq.merge` + スライス代入 | O(m+n) | O(m+n) | 一時リストが発生 |
167158

168159
---
169160

170-
<h2 id="impl">Python 実装</h2>
161+
## Implementation
162+
163+
### Python 実装
171164

172165
```python
173166
from __future__ import annotations
@@ -180,7 +173,7 @@ class Solution:
180173
"""
181174
2 つのソート済み配列を nums1 にインプレースでマージする。
182175
183-
後ろから走査する 3 ポインタ法により O(m+n) 時間・O(1) 追加空間を実現
176+
後ろから走査する 3 ポインタ法により O(m+n) 時間・O(n) 空間(スライス代入による最悪ケース)を実現
184177
185178
不変条件: k >= i が常に成立するため nums1 の未処理要素を上書きしない。
186179
初期値 k - i = n >= 0 が各イテレーションで保持される。
@@ -195,7 +188,7 @@ class Solution:
195188
None(nums1 を破壊的に変更)
196189
197190
Time Complexity: O(m + n)
198-
Space Complexity: O(1)
191+
Space Complexity: O(n)
199192
"""
200193
# ── エッジケース: nums2 が空なら nums1 はそのまま ──────────────────
201194
if n == 0:
@@ -225,51 +218,7 @@ class Solution:
225218
nums1[: j + 1] = nums2[: j + 1]
226219
```
227220

228-
---
229-
230-
<h2 id="cpython">CPython 最適化ポイント</h2>
231-
232-
### 1. スライス代入による残余コピーの高速化
233-
234-
```python
235-
nums1[: j + 1] = nums2[: j + 1]
236-
```
237-
238-
CPython の `list.__setitem__` スライス版は内部で `list_ass_slice` を呼び出し、
239-
`memmove` 相当の C レイヤーのメモリコピーが走る。
240-
逐次 `while j >= 0: nums1[k] = nums2[j]; j -= 1; k -= 1` より
241-
**インタープリタのバイトコードディスパッチ回数を大幅に削減**できる。
242-
243-
### 2. LOAD_FAST によるローカル変数の高速アクセス
244-
245-
| アクセス種別 | バイトコード | 速度 |
246-
| ----------------------------------------------- | ------------- | -------------------- |
247-
| ローカル変数(`i`, `j`, `k`, `nums1`, `nums2`| `LOAD_FAST` | 高速(辞書探索なし) |
248-
| グローバル変数 | `LOAD_GLOBAL` | やや低速 |
249-
| 属性アクセス(`self.xxx`| `LOAD_ATTR` | 最も低速 |
250-
251-
本実装では全カウンタがメソッドのローカルスコープに収まるため、
252-
`LOAD_FAST` が全アクセスに適用される。
253-
254-
### 3. 早期リターンによる不要な処理の排除
255-
256-
```python
257-
if n == 0:
258-
return
259-
```
260-
261-
`n == 0` の場合はループに入らず即リターン。
262-
条件チェックのオーバーヘッドが極めて小さい。
263-
264-
### 4. 型ヒントとランタイムの分離
265-
266-
`from __future__ import annotations` を使用することで、
267-
型アノテーションの評価が **遅延評価(文字列化)** になり、
268-
実行時のインポートオーバーヘッドを最小化する。
269-
270-
---
271-
272-
<h2 id="edgecases">エッジケースと検証観点</h2>
221+
### エッジケースと検証観点
273222

274223
| ケース | 入力例 | 期待出力 | 対応箇所 |
275224
| ------------------------------------- | ------------------------------------------- | -------------- | -------------------------------------- |
@@ -281,15 +230,13 @@ if n == 0:
281230
| 負の数を含む | `nums1=[-3,-1,0,0], m=2, nums2=[-2,0], n=2` | `[-3,-2,-1,0]` | 整数比較なので問題なし |
282231
| 最大制約 | `m=n=100` | 正しくマージ | O(m+n) で余裕 |
283232

284-
### 静的解析観点(Pylance strict mode)
233+
#### 静的解析観点(Pylance strict mode)
285234

286235
- `List[int]` 型ヒントにより `nums1[k]` への `int` 代入が型安全
287236
- `-> None` 明示により暗黙的な `None` 返却が警告なし
288237
- `from __future__ import annotations` で前方参照の問題を回避
289238

290-
---
291-
292-
<h2 id="faq">FAQ</h2>
239+
### FAQ
293240

294241
**Q1. なぜ前からではなく後ろからマージするのか?**
295242

@@ -305,8 +252,8 @@ if n == 0:
305252
**Q3. スライス代入 `nums1[:j+1] = nums2[:j+1]` で一時オブジェクトは発生しないのか?**
306253

307254
右辺 `nums2[:j+1]` がリストスライスとして一時的に生成される(O(j) サイズ)。
308-
ただし `j` は最大でも `n-1` であり定数スケールのため、実質 O(1) の追加コスト
309-
逐次代入に比べてバイトコードのオーバーヘッドを削減できるため、実用上は高速
255+
`j` は最大で `n-1` となるため、最悪ケースでは O(n) の追加空間を消費する
256+
厳密な O(1) が必要な場合は while ループによる各要素コピーが推奨されるが、逐次代入に比べてバイトコードのオーバーヘッドを削減できるため、Python 実用上はスライスが高速
310257

311258
**Q4. `.sort()` を使う方法ではダメなのか?**
312259

@@ -336,3 +283,47 @@ O(m+n) だが一時リストが O(m+n) の追加空間を消費する。
336283

337284
`nums1[i] >= nums2[j]` の条件により、等値の場合は `nums1` 側が優先される。
338285
問題仕様上、安定性(相対順序の保持)は要求されていないため、どちらでも正解。
286+
287+
---
288+
289+
## Optimization
290+
291+
### CPython 最適化ポイント
292+
293+
#### 1. スライス代入による残余コピーの高速化
294+
295+
```python
296+
nums1[: j + 1] = nums2[: j + 1]
297+
```
298+
299+
CPython の `list.__setitem__` スライス版は内部で `list_ass_slice` を呼び出し、
300+
`memmove` 相当の C レイヤーのメモリコピーが走る。
301+
逐次 `while j >= 0: nums1[k] = nums2[j]; j -= 1; k -= 1` より
302+
**インタープリタのバイトコードディスパッチ回数を大幅に削減**できる。
303+
304+
#### 2. LOAD_FAST によるローカル変数の高速アクセス
305+
306+
| アクセス種別 | バイトコード | 速度 |
307+
| ----------------------------------------------- | ------------- | -------------------- |
308+
| ローカル変数(`i`, `j`, `k`, `nums1`, `nums2`| `LOAD_FAST` | 高速(辞書探索なし) |
309+
| グローバル変数 | `LOAD_GLOBAL` | やや低速 |
310+
| 属性アクセス(`self.xxx`| `LOAD_ATTR` | 最も低速 |
311+
312+
本実装では全カウンタがメソッドのローカルスコープに収まるため、
313+
`LOAD_FAST` が全アクセスに適用される。
314+
315+
#### 3. 早期リターンによる不要な処理の排除
316+
317+
```python
318+
if n == 0:
319+
return
320+
```
321+
322+
`n == 0` の場合はループに入らず即リターン。
323+
条件チェックのオーバーヘッドが極めて小さい。
324+
325+
#### 4. 型ヒントとランタイムの分離
326+
327+
`from __future__ import annotations` を使用することで、
328+
型アノテーションの評価が **遅延評価(文字列化)** になり、
329+
実行時のインポートオーバーヘッドを最小化する。

Algorithm/Sort/MergeSort/leetcode/claude 4.6 sonnet extended/88. Merge Sorted Array/README_React.html

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ <h3 class="text-teal-800 font-bold text-lg mb-3">核心アイデア</h3>
837837
{
838838
step: 4,
839839
title: '比較: nums1[2]=3 vs nums2[0]=2',
840-
desc: '3 &ge; 2 なので nums1[2]=3 を nums1[3] に書き込む。i-- → i=1, k-- → k=2。',
840+
desc: '3 2 なので nums1[2]=3 を nums1[3] に書き込む。i-- → i=1, k-- → k=2。',
841841
nums1: [1, 2, 3, 3, 5, 6],
842842
nums2: [2, 5, 6],
843843
i: 1,
@@ -849,7 +849,7 @@ <h3 class="text-teal-800 font-bold text-lg mb-3">核心アイデア</h3>
849849
{
850850
step: 5,
851851
title: '比較: nums1[1]=2 vs nums2[0]=2',
852-
desc: '2 &ge; 2(等値は nums1 側優先)なので nums1[1]=2 を nums1[2] に書き込む。i-- → i=0, k-- → k=1。',
852+
desc: '2 2(等値は nums1 側優先)なので nums1[1]=2 を nums1[2] に書き込む。i-- → i=0, k-- → k=1。',
853853
nums1: [1, 2, 2, 3, 5, 6],
854854
nums2: [2, 5, 6],
855855
i: 0,
@@ -973,7 +973,7 @@ <h3 class="text-teal-800 font-bold text-lg mb-3">核心アイデア</h3>
973973
})}
974974

975975
{/* i pointer */}
976-
{i >= 0 && i < 6 && phase !== 'done' && (
976+
{i >= 0 && i < N1 && phase !== 'done' && (
977977
<g>
978978
<line
979979
x1={startX + i * (CW + GAP) + CW / 2}
@@ -996,7 +996,7 @@ <h3 class="text-teal-800 font-bold text-lg mb-3">核心アイデア</h3>
996996
</g>
997997
)}
998998
{/* k pointer */}
999-
{k >= 0 && k < 6 && phase !== 'done' && (
999+
{k >= 0 && k < N1 && phase !== 'done' && (
10001000
<g>
10011001
<line
10021002
x1={startX + k * (CW + GAP) + CW / 2}
@@ -1063,7 +1063,7 @@ <h3 class="text-teal-800 font-bold text-lg mb-3">核心アイデア</h3>
10631063
})}
10641064

10651065
{/* j pointer */}
1066-
{j >= 0 && j < 3 && (
1066+
{j >= 0 && j < N2 && (
10671067
<g>
10681068
<line
10691069
x1={startX + j * (CW + GAP) + CW / 2}
@@ -1229,8 +1229,9 @@ <h3 className="mt-0 text-teal-800 text-lg font-bold mb-2">
12291229
</h3>
12301230
<p
12311231
className="text-slate-600 leading-7 text-sm"
1232-
dangerouslySetInnerHTML={{ __html: cur.desc }}
1233-
/>
1232+
>
1233+
{cur.desc}
1234+
</p>
12341235
</div>
12351236

12361237
<ArrayViz

0 commit comments

Comments
 (0)