Skip to content

Commit 6ffa54f

Browse files
committed
refactor: refine LeetCode 83 documentation structure, UI accessibility, and optimize SQL/JS solutions
1 parent 8b3644e commit 6ffa54f

6 files changed

Lines changed: 97 additions & 100 deletions

File tree

Algorithm/Other/leetcode/83. Remove Duplicates from Sorted List/Claude 4.6 extended/README.md

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22

33
## 目次(Table of Contents)
44

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

1518
---
1619

17-
<h2 id="overview">概要</h2>
20+
<h2 id="overview">Overview</h2>
1821

1922
### 問題要約
2023

@@ -36,7 +39,9 @@
3639

3740
---
3841

39-
<h2 id="tldr">アルゴリズム要点(TL;DR)</h2>
42+
<h2 id="algorithm">Algorithm</h2>
43+
44+
<h3 id="tldr">アルゴリズム要点(TL;DR)</h3>
4045

4146
- **戦略**: ソート済みであることを活かし、**隣接する重複を 1 パスで除去**する
4247
- **データ構造**: `current` ポインタ 1 本のみ(追加構造なし)
@@ -47,9 +52,9 @@
4752

4853
---
4954

50-
<h2 id="figures">図解</h2>
55+
<h3 id="figures">図解</h3>
5156

52-
### フローチャート
57+
#### フローチャート
5358

5459
```mermaid
5560
flowchart TD
@@ -70,7 +75,7 @@ flowchart TD
7075
7176
---
7277

73-
### データフロー図(具体例)
78+
#### データフロー図(具体例)
7479

7580
```mermaid
7681
graph LR
@@ -106,7 +111,7 @@ graph LR
106111
107112
---
108113

109-
### ASCII ポインタ操作図
114+
#### ASCII ポインタ操作図
110115

111116
```
112117
【初期状態】
@@ -141,9 +146,9 @@ Step 5: cur.next is None → ループ終了
141146

142147
---
143148

144-
<h2 id="correctness">正しさのスケッチ</h2>
149+
<h3 id="correctness">正しさのスケッチ</h3>
145150

146-
### 不変条件
151+
#### 不変条件
147152

148153
> ループの各反復開始時点で、`head` から `current`(含む)までの部分列には重複がない。
149154
@@ -153,19 +158,43 @@ Step 5: cur.next is None → ループ終了
153158
| **維持** | `current.val == nxt.val` なら `nxt` をスキップ(不変条件を保ちつつ次を確認)。異なれば `current` を前進(不変条件は維持される) |
154159
| **終了** | `current.next is None` でループを抜けると、リスト全体で不変条件が成立 |
155160

156-
### 網羅性
161+
#### 網羅性
157162

158163
- 重複を検出するたびに `next` を付け替え → 検出漏れなし(ソート済みなので隣接比較で十分)
159164
- `current` が前進するのは値が異なる場合のみ → 3連続以上の重複も正しく処理される
160165

161-
### 終了性
166+
#### 終了性
162167

163168
- `current` は前進するか、`next` ポインタが短縮されるかのいずれか
164169
- リストは有限長 → 必ず `current.next is None` に到達してループ終了
165170

166171
---
167172

168-
<h2 id="complexity">計算量</h2>
173+
<h3 id="faq">FAQ</h3>
174+
175+
**Q1. `current` を重複スキップ時に前進させない理由は?**
176+
177+
> ソート済みリストで 3 つ以上同値が連続する場合(例: `[1, 1, 1]`)、1 回スキップしても次も同値の可能性がある。`current` を動かさず再チェックすることで、連続する全重複を正しく除去できる。
178+
179+
**Q2. スキップされたノード(旧 `next`)のメモリはどうなる?**
180+
181+
> Python は参照カウント方式の GC を持つ。`current.next = nxt.next``nxt` への参照が消えると、`nxt` の参照カウントが 0 になり即座に解放される(CPython の場合)。
182+
183+
**Q3. なぜ再帰で実装しないのか?**
184+
185+
> 再帰版は可読性が高いが、呼び出しスタックを `O(n)` 消費する。本問題は `n ≤ 300` なので実用上問題ないが、反復版の方がスタックオーバーフローリスクがなく、空間計算量も `O(1)` と優れるため反復を選択した。
186+
187+
**Q4. `head.next is None` のガードは本当に必要か?**
188+
189+
> 厳密にはなくても動作する(`while current.next is not None` が即座にスキップされるため)。しかし「ノードが 1 つ以下なら変更不要」という意図を明示することで可読性と保守性が向上するため、明示的に記述している。
190+
191+
**Q5. 競技プログラミング版と業務開発版の実行速度差は?**
192+
193+
> `n ≤ 300` の小規模入力では測定誤差レベルの差しか生じない。大規模入力(n が数万〜数十万)では `LOAD_ATTR` のキャッシュ効果が現れ始めるが、本問題の制約では本質的な差はない。業務コードでは可読性・型安全性を優先した実装を推奨する。
194+
195+
---
196+
197+
<h2 id="complexity">Complexity</h2>
169198

170199
| 指標 || 理由 |
171200
| -------------- | ------ | ------------------------------------------------------- |
@@ -182,7 +211,9 @@ Step 5: cur.next is None → ループ終了
182211

183212
---
184213

185-
<h2 id="impl">Python 実装</h2>
214+
<h2 id="implementation">Implementation</h2>
215+
216+
<h3 id="impl">Python 実装</h3>
186217

187218
```python
188219
from __future__ import annotations
@@ -288,11 +319,27 @@ class Solution:
288319
return head
289320
```
290321

322+
<h3 id="edgecases">エッジケースと検証観点</h3>
323+
324+
| ケース | 入力 | 期待出力 | 対処 |
325+
| ------------ | ----------------------- | ------------ | ----------------------------------- |
326+
| 空リスト | `head = None` | `None` | ガード節で即時 return |
327+
| ノード 1 つ | `[5]` | `[5]` | `head.next is None` で即時 return |
328+
| 全て同値 | `[3, 3, 3]` | `[3]` | inner while が連続スキップ |
329+
| 全て異なる | `[1, 2, 3]` | `[1, 2, 3]` | 重複検出なし、そのまま return |
330+
| 2 ノード重複 | `[1, 1]` | `[1]` | 1 回スキップして終了 |
331+
| 先頭のみ重複 | `[1, 1, 2, 3]` | `[1, 2, 3]` | Step 1 でスキップ |
332+
| 末尾のみ重複 | `[1, 2, 3, 3]` | `[1, 2, 3]` | 最終ステップでスキップ |
333+
| 最大制約 | n = 300, 全値 −100〜100 | 重複除去済み | O(n) で問題なし |
334+
| 負の値を含む | `[-3, -3, 0, 1, 1]` | `[-3, 0, 1]` | `==` 比較なので値の正負に依存しない |
335+
291336
---
292337

293-
<h2 id="cpython">CPython 最適化ポイント</h2>
338+
<h2 id="optimization">Optimization</h2>
339+
340+
<h3 id="cpython">CPython 最適化ポイント</h3>
294341

295-
### 属性アクセスのキャッシュ
342+
#### 属性アクセスのキャッシュ
296343

297344
```python
298345
# ❌ 遅い: 毎回 LOAD_ATTR が 2 回発生
@@ -315,7 +362,7 @@ while current.next is not None:
315362
| `lru_cache` | 再帰的メモ化 | 本問題は不要 — |
316363
| `bisect` | ソート済み配列への二分探索 | 本問題は不要 — |
317364

318-
### なぜ配列変換しないか
365+
#### なぜ配列変換しないか
319366

320367
```python
321368
# ❌ 配列変換+再構築(O(n) 追加メモリ)
@@ -329,43 +376,3 @@ while cur:
329376
```
330377

331378
ポインタ付け替えのみなら新規オブジェクト生成ゼロで、GC 負荷も最小になる。
332-
333-
---
334-
335-
<h2 id="edgecases">エッジケースと検証観点</h2>
336-
337-
| ケース | 入力 | 期待出力 | 対処 |
338-
| ------------ | ----------------------- | ------------ | ----------------------------------- |
339-
| 空リスト | `head = None` | `None` | ガード節で即時 return |
340-
| ノード 1 つ | `[5]` | `[5]` | `head.next is None` で即時 return |
341-
| 全て同値 | `[3, 3, 3]` | `[3]` | inner while が連続スキップ |
342-
| 全て異なる | `[1, 2, 3]` | `[1, 2, 3]` | 重複検出なし、そのまま return |
343-
| 2 ノード重複 | `[1, 1]` | `[1]` | 1 回スキップして終了 |
344-
| 先頭のみ重複 | `[1, 1, 2, 3]` | `[1, 2, 3]` | Step 1 でスキップ |
345-
| 末尾のみ重複 | `[1, 2, 3, 3]` | `[1, 2, 3]` | 最終ステップでスキップ |
346-
| 最大制約 | n = 300, 全値 −100〜100 | 重複除去済み | O(n) で問題なし |
347-
| 負の値を含む | `[-3, -3, 0, 1, 1]` | `[-3, 0, 1]` | `==` 比較なので値の正負に依存しない |
348-
349-
---
350-
351-
<h2 id="faq">FAQ</h2>
352-
353-
**Q1. `current` を重複スキップ時に前進させない理由は?**
354-
355-
> ソート済みリストで 3 つ以上同値が連続する場合(例: `[1, 1, 1]`)、1 回スキップしても次も同値の可能性がある。`current` を動かさず再チェックすることで、連続する全重複を正しく除去できる。
356-
357-
**Q2. スキップされたノード(旧 `next`)のメモリはどうなる?**
358-
359-
> Python は参照カウント方式の GC を持つ。`current.next = nxt.next``nxt` への参照が消えると、`nxt` の参照カウントが 0 になり即座に解放される(CPython の場合)。
360-
361-
**Q3. なぜ再帰で実装しないのか?**
362-
363-
> 再帰版は可読性が高いが、呼び出しスタックを `O(n)` 消費する。本問題は `n ≤ 300` なので実用上問題ないが、反復版の方がスタックオーバーフローリスクがなく、空間計算量も `O(1)` と優れるため反復を選択した。
364-
365-
**Q4. `head.next is None` のガードは本当に必要か?**
366-
367-
> 厳密にはなくても動作する(`while current.next is not None` が即座にスキップされるため)。しかし「ノードが 1 つ以下なら変更不要」という意図を明示することで可読性と保守性が向上するため、明示的に記述している。
368-
369-
**Q5. 競技プログラミング版と業務開発版の実行速度差は?**
370-
371-
> `n ≤ 300` の小規模入力では測定誤差レベルの差しか生じない。大規模入力(n が数万〜数十万)では `LOAD_ATTR` のキャッシュ効果が現れ始めるが、本問題の制約では本質的な差はない。業務コードでは可読性・型安全性を優先した実装を推奨する。

Algorithm/Other/leetcode/83. Remove Duplicates from Sorted List/Claude 4.6 extended/README_React.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -723,8 +723,7 @@ <h3 class="outfit font-bold text-teal-800 text-lg mb-3">アプローチ比較</h
723723
},
724724
];
725725

726-
// ─── Node visualization ───────────────────────────────────────
727-
function NodeVis({ nodes, step }) {
726+
function NodeVis({ nodes, highlight }) {
728727
const BOX = 52,
729728
GAP = 16,
730729
ARR = 20;
@@ -737,7 +736,7 @@ <h3 class="outfit font-bold text-teal-800 text-lg mb-3">アプローチ比較</h
737736
let x = 20;
738737
allNodes.forEach((n, i) => {
739738
xPositions.push(x);
740-
if (!n.skip) x += BOX + GAP + ARR;
739+
x += BOX + GAP + ARR; // Always advance x for skipped nodes too
741740
});
742741

743742
return (
@@ -804,7 +803,7 @@ <h3 class="outfit font-bold text-teal-800 text-lg mb-3">アプローチ比較</h
804803
);
805804
}
806805
const nx = xPositions[i];
807-
const isActive = i === step - 1 && step > 0;
806+
const isActive = highlight && highlight.includes(i);
808807
// find next non-skipped node
809808
let hasNext = false;
810809
for (let j = i + 1; j < allNodes.length; j++) {
@@ -990,7 +989,7 @@ <h3 className="mt-0 mb-3 text-teal-800 text-base font-bold outfit">
990989

991990
{/* Node visualization */}
992991
<div className="bg-slate-50 rounded-xl p-4 border border-slate-200 mb-4 overflow-x-auto">
993-
<NodeVis nodes={cur.nodes} step={cur.highlight[0] ?? -1} />
992+
<NodeVis nodes={cur.nodes} highlight={cur.highlight} />
994993
</div>
995994

996995
{/* Controls */}

Algorithm/Other/leetcode/83. Remove Duplicates from Sorted List/Claude 4.6 extended/Remove_Duplicates_from_Sorted_List_Rust.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl Solution {
9595

9696
// ── ガード節 ────────────────────────────────────────────────────
9797
// 空リスト or ノード1つ → 重複なし、所有権をそのまま返す
98-
if matches!(head, None | Some(ref node) if node.next.is_none()) {
98+
if head.is_none() || head.as_ref().unwrap().next.is_none() {
9999
return head;
100100
}
101101

JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,11 @@ def debounce(fn: Callable[..., Any], t: float) -> Callable[..., None]:
182182

183183
# 新しいタイマーをセット(t/1000 秒後に fn を実行)
184184
# threading.Timer は秒単位なので、ミリ秒を秒に変換
185-
timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
186-
timer.start()
185+
local_timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
186+
timer = local_timer
187+
188+
# ロック外でタイマーを開始して、ロックの保持時間を最小化する
189+
local_timer.start()
187190

188191
return debounced_func
189192

@@ -195,7 +198,6 @@ class Solution:
195198
実際のLeetCodeにはこの問題はJavaScript/TypeScriptのみだが、
196199
Pythonで同等の機能を提供
197200
"""
198-
from threading import Timer, Lock
199201

200202
def debounce(self, fn: Callable[..., Any], t: int) -> Callable[..., None]:
201203
"""
@@ -218,8 +220,10 @@ class Solution:
218220

219221
# 遷移: 新しいタイマーを作成して開始
220222
# t ミリ秒 = t/1000 秒
221-
timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
222-
timer.start()
223+
local_timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
224+
timer = local_timer
225+
226+
local_timer.start()
223227

224228
return debounced
225229

@@ -336,8 +340,10 @@ class Debouncer:
336340
with self.lock:
337341
if self.timer:
338342
self.timer.cancel()
339-
self.timer = Timer(self.t / 1000.0, self.fn, args, kwargs)
340-
self.timer.start()
343+
local_timer = Timer(self.t / 1000.0, self.fn, args, kwargs)
344+
self.timer = local_timer
345+
346+
local_timer.start()
341347
```
342348

343349
### 4. GIL(Global Interpreter Lock)の影響
@@ -397,7 +403,7 @@ t1.start()
397403
t2.start()
398404
```
399405

400-
**注意**: `threading.Timer` 自体はスレッドセーフだが、`nonlocal timer` への同時アクセスは保護されていない。本格的なマルチスレッド環境では `Lock` が必要
406+
**注意**: 本実装は `threading.Lock` を使用して `timer` への同時アクセスを保護しているためスレッドセーフです
401407

402408
### 6. メモリリーク防止
403409

SQL/Leetcode/Intermediate Join/1164. Product Price at a Given Date/Claude Sonnet 4.5 Extended/Product_Price_at_a_Given_Date_pandas.md

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def price_at_given_date(products: pd.DataFrame) -> pd.DataFrame:
4646

4747
# --- 各製品の最新価格を取得(groupby + idxmax)
4848
if not before_target.empty:
49-
# 同じ日付の場合は最新のインデックスを使用
50-
latest_idx = before_target.sort_values('change_date').groupby('product_id').tail(1).index
49+
# idxmax() で O(N) にて各製品の最新日付のインデックスを取得(同日の場合は最初の行を使用)
50+
latest_idx = before_target.groupby('product_id')['change_date'].idxmax()
5151

5252
latest_prices = before_target.loc[latest_idx, ['product_id', 'new_price']]
5353
else:
@@ -190,20 +190,6 @@ ID: bierner.markdown-mermaid
190190

191191
---
192192

193-
## 📊 セクション分類表
194-
195-
| セクション | 形式 | 記法 |
196-
| -------------- | ------------------- | ---------------------- |
197-
| 見出し・説明文 | **Markdown** | そのまま記述 |
198-
| 実装コード | **コードブロック** | ` ```python ` |
199-
| テスト実行例 | **コードブロック** | ` ```python ` |
200-
| 出力結果 | **コードブロック** | ` ``` ` (言語指定なし) |
201-
| フローチャート | **Mermaidブロック** | ` ```mermaid ` |
202-
|| **Markdown** | `\| 列1 \| 列2 \|` |
203-
| 箇条書き | **Markdown** | `*` or `-` or `1.` |
204-
205-
---
206-
207193
## 🚀 別ファイルで実行する場合
208194

209195
### price_solution.py
@@ -234,4 +220,4 @@ def price_at_given_date(products: pd.DataFrame) -> pd.DataFrame:
234220
'product_id': all_products['product_id'],
235221
'price': all_products['product_id'].map(price_mapper).fillna(10).astype(int)
236222
})
237-
```
223+
```

0 commit comments

Comments
 (0)