diff --git a/.hintrc b/.hintrc new file mode 100644 index 00000000..cb34607d --- /dev/null +++ b/.hintrc @@ -0,0 +1,8 @@ +{ + "extends": [ + "development" + ], + "hints": { + "no-inline-styles": "off" + } +} \ No newline at end of file diff --git a/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/FlattenDeeplyNestedArray_TS.ipynb b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/FlattenDeeplyNestedArray_TS.ipynb new file mode 100644 index 00000000..0485c2e4 --- /dev/null +++ b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/FlattenDeeplyNestedArray_TS.ipynb @@ -0,0 +1,548 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "af2d8ffe", + "metadata": {}, + "source": [ + "# TypeScript コーディング問題の解答\n", + "\n", + "## 1. 問題の分析\n", + "\n", + "### 競技プログラミング視点での分析\n", + "- **実行速度優先**: 再帰呼び出しのオーバーヘッドを最小化し、配列操作を効率化\n", + "- **メモリ使用量**: 結果配列の事前確保は困難なため、動的に構築。コールスタックの深さはO(depth)\n", + "- **制約**: 最大深度1000、要素数最大10^5 → スタックオーバーフローのリスクは低い\n", + "\n", + "### 業務開発視点での分析\n", + "- **型安全性**: TypeScriptの型システムを活用し、再帰型定義で配列構造を表現\n", + "- **可読性**: 再帰的なアプローチが問題の性質に合致し、理解しやすい\n", + "- **エラーハンドリング**: 入力検証とエッジケース処理(空配列、n=0など)\n", + "\n", + "### TypeScript特有の考慮点\n", + "- **再帰型定義**: `MultiDimensionalArray` は自己参照型で型安全性を確保\n", + "- **型ガード**: `Array.isArray()` による実行時型チェック\n", + "- **イミュータブル性**: 元の配列を変更せず新しい配列を返す\n", + "\n", + "## 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n", + "|---------|----------|-----------|------------|---------|-------|-----|\n", + "| 再帰的展開 | O(N) | O(N + D) | 低 | 高 | 高 | N=全要素数、D=深さ。最も直感的 |\n", + "| スタック反復 | O(N) | O(N + D) | 中 | 高 | 中 | スタックオーバーフロー回避可能 |\n", + "| reduce連鎖 | O(N) | O(N + D) | 中 | 中 | 中 | 関数型スタイル、やや複雑 |\n", + "\n", + "## 3. 選択したアルゴリズムと理由\n", + "\n", + "### 選択したアプローチ\n", + "**再帰的展開アプローチ**\n", + "\n", + "### 理由\n", + "- **計算量的優位性**: 全アプローチがO(N)で同等だが、再帰は最もシンプル\n", + "- **TypeScript環境での型安全性**: 再帰型定義との相性が良く、型推論が効果的に働く\n", + "- **保守性・可読性**: 問題の再帰的性質を直接コードに反映でき、理解しやすい\n", + "\n", + "### TypeScript特有の最適化ポイント\n", + "- **型ガードの活用**: `Array.isArray()` で実行時に安全に型を絞り込み\n", + "- **const変数**: イミュータブルな操作を保証\n", + "- **スプレッド構文**: 配列のコピーと結合を型安全に実行\n", + "\n", + "## 4. 実装コード\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 156 ms\n", + "// Beats 38.84%\n", + "// Memory 90.72 MB\n", + "// Beats 60.70%\n", + "\n", + "type MultiDimensionalArray = (number | MultiDimensionalArray)[];\n", + "\n", + "/**\n", + " * 多次元配列を指定された深さまで平坦化する\n", + " * @param arr - 平坦化する多次元配列\n", + " * @param n - 平坦化する深さ(0の場合は平坦化しない)\n", + " * @returns 平坦化された配列\n", + " * @complexity Time: O(N), Space: O(N + D) - N:全要素数, D:深さ\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " // n = 0 の場合は平坦化不要\n", + " if (n === 0) {\n", + " return arr;\n", + " }\n", + " \n", + " const result: MultiDimensionalArray = [];\n", + " \n", + " for (const item of arr) {\n", + " // 配列かつ深さがまだ残っている場合は再帰的に平坦化\n", + " if (Array.isArray(item) && n > 0) {\n", + " // 再帰呼び出しで1レベル深く(深さを1減らす)\n", + " const flattened = flat(item, n - 1);\n", + " // スプレッド構文で結果配列に展開\n", + " result.push(...flattened);\n", + " } else {\n", + " // 配列でないか、深さ制限に達した場合はそのまま追加\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "### 代替実装:メモリ最適化版(事前サイズ計算なし)\n", + "\n", + "```typescript\n", + "// Time Limit Exceeded\n", + "// 127 / 131 testcases passed\n", + "\n", + "/**\n", + " * reduceを使った関数型スタイルの実装\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flatReduce = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " if (n === 0) return arr;\n", + " \n", + " return arr.reduce((acc, item) => {\n", + " if (Array.isArray(item) && n > 0) {\n", + " return acc.concat(flatReduce(item, n - 1));\n", + " }\n", + " return acc.concat(item);\n", + " }, []);\n", + "};\n", + "```\n", + "\n", + "### 代替実装:スタック反復版(スタックオーバーフロー回避)\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 100 ms\n", + "// Beats 73.26%\n", + "// Memory 81.66 MB\n", + "// Beats 75.35%\n", + "\n", + "/**\n", + " * 反復的なスタックベース実装(大規模データ対応)\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flatIterative = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " const result: MultiDimensionalArray = [];\n", + " const stack: Array<{ item: number | MultiDimensionalArray; depth: number }> = [];\n", + " \n", + " // 初期化:全要素を深さ0でスタックに積む\n", + " for (let i = arr.length - 1; i >= 0; i--) {\n", + " stack.push({ item: arr[i], depth: 0 });\n", + " }\n", + " \n", + " while (stack.length > 0) {\n", + " const { item, depth } = stack.pop()!;\n", + " \n", + " if (Array.isArray(item) && depth < n) {\n", + " // 配列を逆順でスタックに積む(元の順序を保持)\n", + " for (let i = item.length - 1; i >= 0; i--) {\n", + " stack.push({ item: item[i], depth: depth + 1 });\n", + " }\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "## 5. TypeScript固有の最適化観点\n", + "\n", + "### 型安全性の活用\n", + "\n", + "1. **再帰型定義による構造の表現**\n", + " - `MultiDimensionalArray` の自己参照により、任意深度の配列を型レベルで表現\n", + " - コンパイル時に不正な型の混入を防止\n", + "\n", + "2. **型ガードによる実行時安全性**\n", + " - `Array.isArray()` で配列判定を行い、TypeScriptが自動的に型を絞り込み\n", + " - `number | MultiDimensionalArray` から `MultiDimensionalArray` への安全な型変換\n", + "\n", + "3. **ジェネリクスの制約**\n", + " - 今回は具体的な型が定義済みだが、拡張時にジェネリクスで柔軟性を確保可能\n", + "\n", + "### 実装パターンの選択\n", + "\n", + "- **再帰版**: 最も可読性が高く、型推論が効果的。制約範囲内で安全\n", + "- **reduce版**: 関数型スタイルで副作用なし、やや型注釈が必要\n", + "- **反復版**: 極端に深い配列(depth > 10000など)でも安全動作\n", + "\n", + "### パフォーマンス考察\n", + "\n", + "- **再帰呼び出しコスト**: 現代のJSエンジンは末尾再帰最適化を持たないが、制約範囲(depth ≤ 1000)では問題なし\n", + "- **配列操作**: `push(...array)` は一度に複数要素を追加するため、ループより効率的\n", + "- **メモリ**: 結果配列は避けられないO(N)。コールスタックはO(D)で十分小さい" + ] + }, + { + "cell_type": "markdown", + "id": "d1fdda10", + "metadata": {}, + "source": [ + "## パフォーマンス分析\n", + "\n", + "### 現状の問題点\n", + "\n", + "1. **再帰版(メイン実装)の問題**\n", + " - `result.push(...flattened)` のスプレッド演算子が**大規模配列で非効率**\n", + " - スプレッド展開は内部的に全要素をコピーするため、配列サイズに比例してコストが増加\n", + "\n", + "2. **reduce版の致命的問題**\n", + " - `concat()` が**毎回新しい配列を生成**するため、実質的にO(N²)の動作\n", + " - Time Limit Exceeded は予想通りの結果\n", + "\n", + "3. **スタック反復版が最速の理由**\n", + " - スプレッド演算子やconcat()を使わず、`push()` で1要素ずつ追加\n", + " - 関数呼び出しのオーバーヘッドがない\n", + "\n", + "## 改善版実装\n", + "\n", + "### 最適化された再帰版(推奨)\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 80 ms\n", + "// Beats 84.88%\n", + "// Memory 76.02 MB\n", + "// Beats 85.12%\n", + "\n", + "type MultiDimensionalArray = (number | MultiDimensionalArray)[];\n", + "\n", + "/**\n", + " * 最適化された再帰的平坦化\n", + " * スプレッド演算子を排除し、ループでpush\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " const result: MultiDimensionalArray = [];\n", + " \n", + " function flatten(items: MultiDimensionalArray, depth: number): void {\n", + " for (const item of items) {\n", + " if (Array.isArray(item) && depth > 0) {\n", + " // 再帰呼び出し(スプレッド演算子を使わない)\n", + " flatten(item, depth - 1);\n", + " } else {\n", + " // 1要素ずつ追加\n", + " result.push(item);\n", + " }\n", + " }\n", + " }\n", + " \n", + " flatten(arr, n);\n", + " return result;\n", + "};\n", + "```\n", + "\n", + "**改善ポイント:**\n", + "- ✅ スプレッド演算子 `...` を完全排除\n", + "- ✅ 内部関数で `result` を共有し、毎回の配列生成を回避\n", + "- ✅ ループで1要素ずつ `push()` することで定数時間操作\n", + "\n", + "### さらに最適化されたスタック反復版\n", + "\n", + "```typescript\n", + "// Wrong Answer\n", + "// 86 / 131 testcases passed\n", + "\n", + "/**\n", + " * 最速のスタック反復実装(改良版)\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " if (n === 0) return arr;\n", + " \n", + " const result: MultiDimensionalArray = [];\n", + " const stack: Array<[MultiDimensionalArray | number, number]> = [[arr, 0]];\n", + " \n", + " while (stack.length > 0) {\n", + " const [item, depth] = stack.pop()!;\n", + " \n", + " if (Array.isArray(item)) {\n", + " if (depth < n) {\n", + " // 逆順でスタックに積む(元の順序を保持)\n", + " for (let i = item.length - 1; i >= 0; i--) {\n", + " stack.push([item[i], depth + 1]);\n", + " }\n", + " } else {\n", + " // 深さ制限到達、配列全体を追加\n", + " result.push(item);\n", + " }\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "**改善ポイント:**\n", + "- ✅ タプル型 `[item, depth]` でオブジェクト生成コストを削減\n", + "- ✅ 初期スタックを `[[arr, 0]]` として1回の push で初期化\n", + "- ✅ 型チェックの回数を最小化\n", + "\n", + "### ハイブリッド実装(浅い配列は再帰、深い配列はスタック)\n", + "\n", + "```typescript\n", + "// Wrong Answer\n", + "// 123 / 131 testcases passed\n", + "\n", + "/**\n", + " * 深さに応じて最適な手法を選択\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " // 浅い平坦化は再帰で高速化(関数呼び出しコストが問題にならない)\n", + " if (n <= 3) {\n", + " const result: MultiDimensionalArray = [];\n", + " \n", + " function flatten(items: MultiDimensionalArray, depth: number): void {\n", + " for (const item of items) {\n", + " if (Array.isArray(item) && depth > 0) {\n", + " flatten(item, depth - 1);\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " }\n", + " \n", + " flatten(arr, n);\n", + " return result;\n", + " }\n", + " \n", + " // 深い平坦化はスタックで安全性確保\n", + " const result: MultiDimensionalArray = [];\n", + " const stack: Array<[MultiDimensionalArray | number, number]> = [[arr, 0]];\n", + " \n", + " while (stack.length > 0) {\n", + " const [item, depth] = stack.pop()!;\n", + " \n", + " if (Array.isArray(item)) {\n", + " if (depth < n) {\n", + " for (let i = item.length - 1; i >= 0; i--) {\n", + " stack.push([item[i], depth + 1]);\n", + " }\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "## 予想されるパフォーマンス\n", + "\n", + "| 実装 | Runtime予想 | Memory予想 | 特徴 |\n", + "|-----|-----------|-----------|------|\n", + "| **最適化再帰版** | ~100-120ms (65-75%) | ~82-85MB (70-75%) | シンプルで可読性高、十分高速 |\n", + "| **改良スタック版** | ~95-105ms (75-80%) | ~80-83MB (75-80%) | 最速候補、やや複雑 |\n", + "| **ハイブリッド版** | ~98-110ms (70-78%) | ~81-84MB (72-77%) | バランス型 |\n", + "\n", + "## 推奨実装\n", + "\n", + "**最適化再帰版**を推奨します:\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 65 ms\n", + "// Beats 98.37%\n", + "// Memory 74.89 MB\n", + "// Beats 90.70%\n", + "\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " const result: MultiDimensionalArray = [];\n", + " \n", + " function flatten(items: MultiDimensionalArray, depth: number): void {\n", + " for (const item of items) {\n", + " if (Array.isArray(item) && depth > 0) {\n", + " flatten(item, depth - 1);\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " }\n", + " \n", + " flatten(arr, n);\n", + " return result;\n", + "};\n", + "```\n", + "\n", + "**理由:**\n", + "- ✅ スタック反復版に匹敵する速度が期待できる\n", + "- ✅ コードが簡潔で保守性が高い\n", + "- ✅ TypeScriptの型推論が効果的に機能\n", + "- ✅ 制約範囲(depth ≤ 1000)で安全" + ] + }, + { + "cell_type": "markdown", + "id": "8ea3662c", + "metadata": {}, + "source": [ + "不正解となった2つの実装の問題点と修正案を提示します。\n", + "\n", + "## 問題の分析\n", + "\n", + "両方とも**スタック初期化の誤り**が原因です。\n", + "\n", + "### 誤りの箇所\n", + "\n", + "```typescript\n", + "// ❌ 間違い:配列全体を1つの要素として扱ってしまう\n", + "const stack: Array<[MultiDimensionalArray | number, number]> = [[arr, 0]];\n", + "```\n", + "\n", + "この初期化では、`arr` 全体が1つの要素として深さ0で処理されます。\n", + "例えば `arr = [1, 2, [3]]` の場合、`arr` そのものが深さ0の配列として扱われ、意図しない展開が発生します。\n", + "\n", + "### 正しい初期化\n", + "\n", + "```typescript\n", + "// ✅ 正しい:配列の各要素を個別にスタックに積む\n", + "const stack: Array<[MultiDimensionalArray | number, number]> = [];\n", + "for (let i = arr.length - 1; i >= 0; i--) {\n", + " stack.push([arr[i], 0]);\n", + "}\n", + "```\n", + "\n", + "## 修正版\n", + "\n", + "### さらに最適化されたスタック反復版(修正版)\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 98 ms\n", + "// Beats 73.49%\n", + "// Memory 84.92 MB\n", + "// Beats 72.56%\n", + "\n", + "/**\n", + " * 修正されたスタック反復実装\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " if (n === 0) return arr;\n", + " \n", + " const result: MultiDimensionalArray = [];\n", + " const stack: Array<[MultiDimensionalArray | number, number]> = [];\n", + " \n", + " // 修正:各要素を個別に深さ0でスタックに積む\n", + " for (let i = arr.length - 1; i >= 0; i--) {\n", + " stack.push([arr[i], 0]);\n", + " }\n", + " \n", + " while (stack.length > 0) {\n", + " const [item, depth] = stack.pop()!;\n", + " \n", + " if (Array.isArray(item) && depth < n) {\n", + " // 配列を逆順でスタックに積む\n", + " for (let i = item.length - 1; i >= 0; i--) {\n", + " stack.push([item[i], depth + 1]);\n", + " }\n", + " } else {\n", + " // 数値または深さ制限到達の配列\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "### ハイブリッド実装(修正版)\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 91 ms\n", + "// Beats 76.98%\n", + "// Memory 84.08 MB\n", + "// Beats 72.56%\n", + "\n", + "/**\n", + " * 深さに応じて最適な手法を選択(修正版)\n", + " * @complexity Time: O(N), Space: O(N + D)\n", + " */\n", + "var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {\n", + " // 浅い平坦化は再帰で高速化\n", + " if (n <= 3) {\n", + " const result: MultiDimensionalArray = [];\n", + " \n", + " function flatten(items: MultiDimensionalArray, depth: number): void {\n", + " for (const item of items) {\n", + " if (Array.isArray(item) && depth > 0) {\n", + " flatten(item, depth - 1);\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " }\n", + " \n", + " flatten(arr, n);\n", + " return result;\n", + " }\n", + " \n", + " // 深い平坦化はスタックで安全性確保\n", + " const result: MultiDimensionalArray = [];\n", + " const stack: Array<[MultiDimensionalArray | number, number]> = [];\n", + " \n", + " // 修正:各要素を個別に深さ0でスタックに積む\n", + " for (let i = arr.length - 1; i >= 0; i--) {\n", + " stack.push([arr[i], 0]);\n", + " }\n", + " \n", + " while (stack.length > 0) {\n", + " const [item, depth] = stack.pop()!;\n", + " \n", + " if (Array.isArray(item) && depth < n) {\n", + " for (let i = item.length - 1; i >= 0; i--) {\n", + " stack.push([item[i], depth + 1]);\n", + " }\n", + " } else {\n", + " result.push(item);\n", + " }\n", + " }\n", + " \n", + " return result;\n", + "};\n", + "```\n", + "\n", + "## 修正内容のまとめ\n", + "\n", + "| 箇所 | 修正前 | 修正後 |\n", + "|-----|-------|-------|\n", + "| スタック初期化 | `[[arr, 0]]` | `arr` の各要素を個別に `[arr[i], 0]` として追加 |\n", + "| depth判定 | `if (depth < n)` のみ | `if (Array.isArray(item) && depth < n)` |\n" + ] + }, + { + "cell_type": "markdown", + "id": "253e2ebb", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "TypeScript", + "language": "typescript", + "name": "typescript" + }, + "language_info": { + "file_extension": ".ts", + "mimetype": "text/typescript", + "name": "typescript", + "version": "5.3.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README.md b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README.md new file mode 100644 index 00000000..5686a3eb --- /dev/null +++ b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README.md @@ -0,0 +1,417 @@ +# Flatten Deeply Nested Array - 再帰的配列平坦化 + +## 目次 + +- [概要](#overview) +- [アルゴリズム要点(TL;DR)](#tldr) +- [図解](#figures) +- [正しさのスケッチ](#correctness) +- [計算量](#complexity) +- [TypeScript実装](#impl) +- [TypeScript最適化ポイント](#typescript) +- [エッジケースと検証観点](#edgecases) +- [FAQ](#faq) + +--- + +

概要

+ +**問題**: LeetCode 2625 - Flatten Deeply Nested Array + +多次元配列 `arr` と深さ `n` を受け取り、指定された深さまで平坦化した配列を返す。平坦化は現在のネスト深度が `n` 未満の場合にのみ実行される。最初の配列の要素は深度 0 とみなされる。 + +**制約**: + +- 組み込みメソッド `Array.flat` の使用は禁止 +- 配列内の数値の数: 0 ≤ count ≤ 10^5 +- サブ配列の数: 0 ≤ count ≤ 10^5 +- 最大深度 maxDepth ≤ 1000 +- 各数値: -1000 ≤ number ≤ 1000 +- 平坦化深度: 0 ≤ n ≤ 1000 + +**要件**: + +- **正当性**: 深度制限 `n` を正確に遵守し、要素の順序を保持 +- **型安全性**: TypeScriptの再帰型定義を活用した型安全な実装 +- **効率性**: O(N) 時間、O(N + D) 空間(N = 全要素数、D = 深度) + +--- + +

アルゴリズム要点(TL;DR)

+ +**戦略**: + +- 再帰的な深さ優先探索により、各要素を走査 +- 配列要素かつ深度制限内であれば再帰呼び出し +- それ以外は結果配列に直接追加 + +**データ構造**: + +- 結果配列: `MultiDimensionalArray` 型(再帰型定義) +- 内部関数でクロージャを活用し、結果配列を共有 + +**計算量**: + +- 時間: **O(N)** - 各要素を1回ずつ訪問(N = 全要素数) +- 空間: **O(N + D)** - 結果配列 O(N) + コールスタック O(D)(D = 最大深度) + +**メモリ最適化**: + +- スプレッド演算子を排除し、`push()` で1要素ずつ追加 +- クロージャで結果配列を共有し、配列の再生成を回避 + +**性能**: + +- Runtime: 80ms (84.88%) +- Memory: 76.02MB (85.12%) + +--- + +

図解

+ +### フローチャート + +```mermaid +flowchart TD + Start[Start flatten] --> InitResult[Initialize result array] + InitResult --> DefineInner[Define inner flatten function] + DefineInner --> CallInner[Call flatten with arr and n] + CallInner --> LoopStart{For each item in items} + LoopStart -- Has more --> CheckArray{Is item array and depth > 0} + CheckArray -- Yes --> Recurse[Recursively call flatten with depth - 1] + Recurse --> LoopStart + CheckArray -- No --> PushItem[Push item to result] + PushItem --> LoopStart + LoopStart -- Done --> ReturnResult[Return result] + ReturnResult --> End[End] +``` + +**説明**: + +- 外部関数で結果配列を初期化し、内部関数 `flatten` を定義 +- 各要素について、配列かつ深度制限内なら再帰呼び出し +- それ以外は結果配列に直接 `push()` +- すべての要素を処理後、結果を返す + +### データフロー図 + +```mermaid +graph LR + subgraph Input_Layer + A[Input arr and n] --> B[Validate depth limit] + end + subgraph Processing_Layer + B --> C[Initialize empty result] + C --> D[Define inner flatten function] + D --> E[Iterate over items] + E --> F{Array check} + F -- Array --> G[Recursive flatten] + F -- Primitive --> H[Direct push] + G --> E + H --> E + end + subgraph Output_Layer + E --> I[Return flattened result] + end +``` + +**説明**: + +- 入力層: 引数の受け取りと初期検証 +- 処理層: 内部関数による再帰的な要素走査とプッシュ +- 出力層: 平坦化された結果配列の返却 + +--- + +

正しさのスケッチ

+ +### 不変条件 + +1. **深度カウンタの正確性**: 再帰呼び出し時に `depth - 1` を渡すことで、現在の深度を正確に追跡 +2. **要素順序の保持**: `for...of` ループで順次処理し、`push()` で順番に追加することで元の順序を維持 +3. **結果の一意性**: クロージャで単一の `result` 配列を共有し、重複や欠損を防止 + +### 網羅性 + +- **配列要素 + 深度制限内**: 再帰呼び出しで展開 +- **配列要素 + 深度制限到達**: そのまま配列として追加 +- **プリミティブ値(数値)**: 深度に関わらず直接追加 +- すべてのケースが `if-else` で網羅されている + +### 基底条件 + +- 各要素について「配列かつ depth > 0」を満たさない場合、再帰を停止 +- 空配列の場合も正しく空配列を返す(初期化された `result` がそのまま返る) + +### 終了性 + +- 各再帰呼び出しで `depth - 1` となるため、必ず 0 以下に収束 +- 配列のサイズは有限であり、`for` ループは必ず終了 +- 制約により最大深度 1000 でスタックオーバーフローのリスクは低い + +--- + +

計算量

+ +### 時間計算量: O(N) + +- **N**: 配列内の全要素数(プリミティブ値 + サブ配列の総数) +- 各要素を1回ずつ訪問し、配列判定と `push()` は O(1) +- したがって全体で O(N) + +### 空間計算量: O(N + D) + +- **結果配列**: O(N) - 全要素を格納 +- **コールスタック**: O(D) - 最大深度までの再帰呼び出し(D ≤ 1000) +- 合計: O(N + D) + +### アプローチ比較 + +| 実装方式 | 時間計算量 | 空間計算量 | 可読性 | 性能(実測) | +| ------------------------ | ---------- | ---------- | ------ | -------------- | +| **最適化再帰版**(推奨) | O(N) | O(N + D) | ★★★★★ | 80ms (84.88%) | +| スプレッド再帰版 | O(N) | O(N + D) | ★★★★☆ | 156ms (38.84%) | +| reduce版 | O(N²) | O(N² + D) | ★★★☆☆ | TLE | +| スタック反復版 | O(N) | O(N + D) | ★★★☆☆ | 100ms (73.26%) | + +**考察**: + +- スプレッド演算子 `...` は大規模配列で非効率(内部コピーのコスト) +- `concat()` は毎回新配列を生成し O(N²) に劣化 +- 最適化再帰版は可読性と性能を両立 + +--- + +

TypeScript実装

+ +```typescript +type MultiDimensionalArray = (number | MultiDimensionalArray)[]; + +/** + * 多次元配列を指定された深さまで平坦化する + * + * @param arr - 平坦化する多次元配列 + * @param n - 平坦化する深さ(0の場合は平坦化しない) + * @returns 平坦化された配列 + * + * @complexity + * Time: O(N) - N は全要素数 + * Space: O(N + D) - N は結果配列、D はコールスタック深度 + * + * @example + * flat([1, 2, [3, 4]], 1) // [1, 2, 3, 4] + * flat([1, [2, [3]]], 1) // [1, 2, [3]] + */ +var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray { + // 結果配列を初期化(クロージャで共有) + const result: MultiDimensionalArray = []; + + /** + * 内部再帰関数:配列を深さ制限付きで平坦化 + * @param items - 処理対象の配列 + * @param depth - 残りの平坦化可能深度 + */ + function flatten(items: MultiDimensionalArray, depth: number): void { + for (const item of items) { + // 配列かつ深度制限内の場合、再帰的に展開 + if (Array.isArray(item) && depth > 0) { + flatten(item, depth - 1); + } else { + // プリミティブまたは深度制限到達の配列をそのまま追加 + result.push(item); + } + } + } + + // 初期呼び出し + flatten(arr, n); + + return result; +}; +``` + +### 実装のポイント + +1. **クロージャの活用**: + - 外部で `result` を宣言し、内部関数 `flatten` から参照 + - 配列の再生成を回避し、メモリ効率を向上 + +2. **型安全性**: + - `MultiDimensionalArray` の再帰型定義により、任意深度の配列を表現 + - `Array.isArray()` による実行時の型ガード + +3. **イミュータブル**: + - 元の配列 `arr` は変更せず、新しい `result` を構築 + - Pure function として副作用なし + +4. **エッジケース処理**: + - `n = 0`: ループが実行されるが、`depth > 0` が常に false となり元の配列が返る + - 空配列: ループが実行されず、空の `result` が返る + +--- + +

TypeScript最適化ポイント

+ +### 1. スプレッド演算子の排除 + +**避けるべきパターン**: + +```typescript +// ❌ 非効率:スプレッド展開で全要素をコピー +result.push(...flattened); +``` + +**推奨パターン**: + +```typescript +// ✅ 効率的:1要素ずつpush(O(1)操作) +for (const item of flattened) { + result.push(item); +} +``` + +**理由**: + +- `push(...array)` は内部的に配列全体をイテレートしてコピー +- 大規模配列では顕著な性能低下(156ms → 80ms) + +### 2. concat() の回避 + +**避けるべきパターン**: + +```typescript +// ❌ O(N²):毎回新配列を生成 +return acc.concat(item); +``` + +**推奨パターン**: + +```typescript +// ✅ O(N):既存配列にpush +result.push(item); +``` + +### 3. 型推論の活用 + +```typescript +// 型注釈は最小限に(TypeScriptが推論) +const result: MultiDimensionalArray = []; // 明示的 +function flatten(items: MultiDimensionalArray, depth: number): void { + // 戻り値 void は推論可能だが、明示することで意図を明確化 +} +``` + +### 4. 型ガードの効果的な利用 + +```typescript +if (Array.isArray(item) && depth > 0) { + // この時点で TypeScript は item を MultiDimensionalArray と認識 + flatten(item, depth - 1); +} +``` + +### 5. Node.js v22 の最適化 + +- **V8エンジン**: 配列操作のJITコンパイル最適化 +- **ESM形式**: Tree-shaking により未使用コードを削減 +- **strict mode**: より効率的な最適化が適用 + +--- + +

エッジケースと検証観点

+ +### 主要エッジケース + +1. **n = 0(平坦化しない)** + - 入力: `[1, [2, [3]]]`, n = 0 + - 出力: `[1, [2, [3]]]` + - 検証: 元の配列がそのまま返る + +2. **空配列** + - 入力: `[]`, n = 任意 + - 出力: `[]` + - 検証: 空配列を正しく処理 + +3. **深い入れ子(maxDepth = 1000)** + - 入力: 1000層の入れ子配列 + - 検証: スタックオーバーフローが発生しない + +4. **n > maxDepth(完全平坦化)** + - 入力: `[1, [2, [3, [4]]]]`, n = 100 + - 出力: `[1, 2, 3, 4]` + - 検証: すべてのネストが解消される + +5. **配列のみの要素** + - 入力: `[[[[]]]]`, n = 2 + - 出力: `[[]]` + - 検証: 空配列が正しく処理される + +6. **大規模データ(10^5要素)** + - 入力: 10万個の要素を持つ配列 + - 検証: メモリ制限内で動作し、タイムアウトしない + +### 境界値テスト + +| ケース | 入力例 | 期待出力 | 検証ポイント | +| -------- | ------------------ | ----------- | ------------ | +| 最小入力 | `[]`, n=0 | `[]` | 空配列処理 | +| n=0 | `[1,[2]]`, n=0 | `[1,[2]]` | 平坦化なし | +| n=1 | `[1,[2,[3]]]`, n=1 | `[1,2,[3]]` | 1層のみ展開 | +| 数値のみ | `[1,2,3]`, n=5 | `[1,2,3]` | 配列なし | +| 深度一致 | `[[[1]]]`, n=2 | `[1]` | 完全展開 | +| 負数含む | `[-1,[-2]]`, n=1 | `[-1,-2]` | 負数処理 | + +--- + +

FAQ

+ +### Q1. なぜ再帰版が最速なのか? + +**A**: スプレッド演算子や `concat()` を排除し、1要素ずつ `push()` することで定数時間操作を実現。クロージャによる配列共有で再生成コストも削減。 + +### Q2. スタックオーバーフローのリスクは? + +**A**: 制約により最大深度は 1000。現代のJavaScriptエンジン(V8)はこの程度の再帰深度を問題なく処理可能。ただし、深度が10000を超える場合は反復版(スタック)を推奨。 + +### Q3. Array.flat() との性能差は? + +**A**: 組み込み `Array.flat()` はC++で実装されており、通常はより高速。ただし、本実装も 84.88% と十分な性能を達成。学習目的では再帰的アプローチの理解が重要。 + +### Q4. なぜ内部関数を使うのか? + +**A**: クロージャで `result` 配列を共有することで、以下の利点が得られる: + +- 配列の再生成を回避(メモリ効率向上) +- Pure function として外部状態を変更しない +- 引数として渡す必要がなく、コード簡潔化 + +### Q5. TypeScriptの型安全性の恩恵は? + +**A**: + +- `MultiDimensionalArray` の再帰型定義により、コンパイル時に型エラーを検出 +- `Array.isArray()` による型ガードで、実行時の型安全性も確保 +- IDEの補完機能が効果的に働き、開発効率向上 + +### Q6. in-place 実装は可能か? + +**A**: この問題では新しい配列を返す仕様のため、in-place は不適切。元の配列を変更すると、再帰呼び出し中に構造が変化し、正しく動作しない。 + +### Q7. メモ化は有効か? + +**A**: この問題では同じ部分配列が複数回処理されることはないため、メモ化は不要。むしろメモリ使用量が増加するだけでデメリットが大きい。 + +### Q8. ジェネリクスで拡張可能か? + +**A**: 現在の型定義は `number` 固定だが、以下のように拡張可能: + +```typescript +type DeepArray = (T | DeepArray)[]; +function flatGeneric(arr: DeepArray, n: number): DeepArray { ... } +``` + +--- + +**作成日**: 2026-02-08 +**LeetCode問題**: [2625. Flatten Deeply Nested Array](https://leetcode.com/problems/flatten-deeply-nested-array/) +**最適解性能**: Runtime 80ms (84.88%), Memory 76.02MB (85.12%) diff --git a/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README_react.html b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README_react.html new file mode 100644 index 00000000..883070aa --- /dev/null +++ b/JavaScript/2625. Flatten Deeply Nested Array/Claude Code Sonnet 4.5 extended/README_react.html @@ -0,0 +1,2009 @@ + + + + + + LeetCode 2625 - Flatten Deeply Nested Array | 再帰的配列平坦化 + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+

+ アルゴリズム概要 +

+ +

問題定義

+

+ 多次元配列 + arr と深さ + n + を受け取り、指定された深さまで平坦化した配列を返します。平坦化は現在のネスト深度が + n + 未満の場合にのみ実行されます。最初の配列の要素は深度 0 とみなされます。 +

+ +

入出力例

+
+

Example 1:

+
入力: arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]], n = 0
+出力: [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]
+説明: n=0 の場合、平坦化されません
+
+ +
+

Example 2:

+
入力: arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]], n = 1
+出力: [1, 2, 3, 4, 5, 6, 7, 8, [9, 10, 11], 12, 13, 14, 15]
+説明: 深度0のサブ配列のみ平坦化されます
+
+ +

制約条件

+
    +
  • 配列内の数値の数: 0 ≤ count ≤ 105
  • +
  • サブ配列の数: 0 ≤ count ≤ 105
  • +
  • 最大深度 maxDepth ≤ 1000
  • +
  • 各数値: -1000 ≤ number ≤ 1000
  • +
  • 平坦化深度: 0 ≤ n ≤ 1000
  • +
  • Array.flat の使用は禁止
  • +
+ +

アルゴリズム戦略

+
+
+

✅ 主要アプローチ

+
    +
  • 再帰的な深さ優先探索
  • +
  • クロージャで結果配列を共有
  • +
  • 1要素ずつpushで効率化
  • +
  • 型安全な再帰型定義
  • +
+
+
+

⚡ 最適化ポイント

+
    +
  • スプレッド演算子を排除
  • +
  • concat()を使わない
  • +
  • 配列の再生成を回避
  • +
  • 定数時間push操作
  • +
+
+
+ +

性能

+
+

+ 🚀 Runtime: 80ms (84.88%) | Memory: + 76.02MB (85.12%) +

+
+
+ + +
+

+ ステップバイステップ解説 +

+
+
+ + +
+

+ TypeScript実装 +

+
type MultiDimensionalArray = (number | MultiDimensionalArray)[];
+
+/**
+ * 多次元配列を指定された深さまで平坦化する
+ *
+ * @param arr - 平坦化する多次元配列
+ * @param n - 平坦化する深さ(0の場合は平坦化しない)
+ * @returns 平坦化された配列
+ *
+ * @complexity
+ * Time: O(N) - N は全要素数
+ * Space: O(N + D) - N は結果配列、D はコールスタック深度
+ *
+ * @example
+ * flat([1, 2, [3, 4]], 1) // [1, 2, 3, 4]
+ * flat([1, [2, [3]]], 1) // [1, 2, [3]]
+ */
+var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {
+    // 結果配列を初期化(クロージャで共有)
+    const result: MultiDimensionalArray = [];
+
+    /**
+     * 内部再帰関数:配列を深さ制限付きで平坦化
+     * @param items - 処理対象の配列
+     * @param depth - 残りの平坦化可能深度
+     */
+    function flatten(items: MultiDimensionalArray, depth: number): void {
+        for (const item of items) {
+            // 配列かつ深度制限内の場合、再帰的に展開
+            if (Array.isArray(item) && depth > 0) {
+                flatten(item, depth - 1);
+            } else {
+                // プリミティブまたは深度制限到達の配列をそのまま追加
+                result.push(item);
+            }
+        }
+    }
+
+    // 初期呼び出し
+    flatten(arr, n);
+
+    return result;
+};
+ +

実装のポイント

+
+
+

1. クロージャの活用

+

+ 外部でresultを宣言し、内部関数から参照することで配列の再生成を回避 +

+
+
+

2. 型安全性

+

+ 再帰型定義により任意深度の配列を表現し、コンパイル時に型エラーを検出 +

+
+
+

3. イミュータブル

+

+ 元の配列を変更せず、新しい結果配列を構築するPure function +

+
+
+

4. エッジケース

+

+ n=0、空配列、深い入れ子などを正しく処理 +

+
+
+
+ + +
+

+ フローチャート +

+
+ + Flatten Deeply Nested Array Flowchart + + A flowchart diagram showing the algorithm flow for flattening a deeply + nested array with depth control + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 開始 + + + flatten(arr, n) + + + + + + + + + 結果配列を初期化 + + + result = [] + + + + + + + + + 内部flatten関数を定義 + + + function flatten(items, depth) + + + + + + + + + 各要素を走査 + + + for (const item of items) + + + + + + 終了 + + + + + + 要素あり + + + + + + 配列 かつ + + + depth > 0? + + + + + + はい + + + + + + 再帰呼び出し + + + flatten(item, depth - 1) + + + + + + + 次の要素へ + + + + + + いいえ + + + + + + 直接追加 + + + result.push(item) + + + + + + + 次の要素へ + + + + + + resultを返す + + + return result + + + + + + + + + 終了 + + +
+ +
+

📊 フローの説明

+
+
+
+ 1 +
+

+ 初期化:結果配列を空配列で初期化し、内部flatten関数を定義します +

+
+
+
+ 2 +
+

+ ループ処理:各要素についてループで走査します(for...of) +

+
+
+
+ 3 +
+

+ 分岐判定:配列かつdepth + > 0の場合は再帰的に展開、そうでなければ直接追加 +

+
+
+
+ 4 +
+

+ ループバック:紫の破線矢印は次の要素への遷移を示します +

+
+
+
+ 5 +
+

+ 完了:すべての要素を処理後、結果配列を返して終了します +

+
+
+ +
+

🎨 色分けルール

+
+
+
+ 緑:開始/終了/成功パス +
+
+
+ 青:処理ステップ +
+
+
+ オレンジ:条件分岐 +
+
+
+ 紫:ループバック +
+
+
+
+
+ + +
+

+ 計算量分析 +

+ +
+
+

⏱️ 時間計算量

+

O(N)

+

+ N = 配列内の全要素数(プリミティブ値 + サブ配列の総数)
+ 各要素を1回ずつ訪問し、配列判定とpush()はO(1)のため全体でO(N) +

+
+ +
+

💾 空間計算量

+

O(N + D)

+

+ 結果配列: O(N) - 全要素を格納
+ コールスタック: O(D) - 最大深度までの再帰呼び出し(D ≤ 1000) +

+
+
+ +

アプローチ比較

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 実装方式 + + 時間計算量 + + 空間計算量 + + 可読性 + + 性能(実測) +
+ 最適化再帰版(推奨) + O(N)O(N + D)★★★★★ + 80ms (84.88%) +
+ スプレッド再帰版 + O(N)O(N + D)★★★★☆ + 156ms (38.84%) +
reduce版 + O(N²) + O(N² + D)★★★☆☆ + TLE +
スタック反復版O(N)O(N + D)★★★☆☆100ms (73.26%)
+
+ +
+

💡 最適化の考察

+
    +
  • + スプレッド演算子...は大規模配列で非効率(内部コピーのコスト) +
  • +
  • + concat()は毎回新配列を生成しO(N²)に劣化 +
  • +
  • 最適化再帰版は可読性と性能を両立
  • +
  • クロージャによる配列共有がメモリ効率の鍵
  • +
+
+
+ + +
+

+ Created: 2026-02-08 | + + LeetCode 2625 + +

+

Runtime: 80ms (84.88%) | Memory: 76.02MB (85.12%)

+
+
+ + + + + + + + + + + + + diff --git a/verify_sri.py b/verify_sri.py deleted file mode 100644 index c0f9ccb5..00000000 --- a/verify_sri.py +++ /dev/null @@ -1,22 +0,0 @@ -import hashlib -import requests -import base64 - -urls = [ - "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css", - "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css", - "https://cdn.tailwindcss.com/3.4.1" -] - -for url in urls: - try: - response = requests.get(url) - content = response.content - hash_obj = hashlib.sha384(content) - base64_hash = base64.b64encode(hash_obj.digest()).decode('utf-8') - print(f"URL: {url}") - print(f"SRI: sha384-{base64_hash}") - print("-" * 20) - except Exception as e: - print(f"Error fetching {url}: {e}")