配列 (Arrays)
2025-08-17
配列についての学習ノート
配列って最初は「ただの箱の並び」だと思ってたけど、実はめちゃくちゃ重要な概念だった。
📚 配列の詳細ページ
配列について深く学ぶために、以下のページに分けてまとめました:
- なぜ配列が高速なのか、メモリレベルで理解する
- 連続メモリの重要性
- アドレス計算の仕組み
- サイズ固定の配列の実装
- 基本操作(挿入、削除、アクセス)
- 時間計算量の詳細
- 自動リサイズの仕組み
- 償却時間計算量の理解
- PythonやJavaScriptの配列の裏側
- LIFO構造の活用
- 括弧チェック、Undo機能の実装
- 関数呼び出しスタックの理解
最初に躓いたこと
インデックスは0から始まる
const arr = [10, 20, 30];
// arr[0] = 10 ← 最初これが違和感あった
// arr[1] = 20
// arr[2] = 30
// arr[3] = undefined ← よくやったミス
最初の頃、配列の長さが5なのに arr[5] にアクセスしようとしてundefinedが返ってきて「???」ってなった。
配列の基本操作で学んだこと
要素の追加・削除
// 末尾に追加 - O(1) 速い!
arr.push(40);
// 先頭に追加 - O(n) 遅い...
arr.unshift(5); // 全要素を右にずらす必要がある
// なるほど、だから配列の先頭操作は避けるべきなのか
実際にやってしまった失敗
// ダメな例:配列を走査しながら要素を削除
const nums = [1, 2, 3, 4, 5];
for (let i = 0; i < nums.length; i++) {
if (nums[i] % 2 === 0) {
nums.splice(i, 1); // インデックスがずれる!
}
}
// 結果:[1, 3, 5] にならず、[1, 3, 4, 5] になる
// 良い例:後ろから走査
for (let i = nums.length - 1; i >= 0; i--) {
if (nums[i] % 2 === 0) {
nums.splice(i, 1);
}
}
二次元配列で混乱した話
// 最初はこう書いてた(間違い)
const matrix = new Array(3).fill(new Array(3).fill(0));
matrix[0][0] = 1;
console.log(matrix[1][0]); // 1 !? なんで?
// 理由:同じ配列の参照をコピーしてた
// 正しい方法
const matrix = Array.from({length: 3}, () => new Array(3).fill(0));
配列のメモリについて理解したこと
配列がなぜ高速なのか:
- 連続したメモリ領域に格納される
- インデックスアクセスが O(1) なのは、
先頭アドレス + (インデックス × 要素サイズ)
で計算できるから - だからこそキャッシュ効率も良い
メモリのイメージ:
[10][20][30][40][50] ← 連続している
↑
arr[0]のアドレスがわかれば、arr[3]は3つ先
実践で使えるテクニック
配列の初期化
// 長さ10、全て0の配列
const zeros = new Array(10).fill(0);
// 1から10までの配列
const nums = Array.from({length: 10}, (_, i) => i + 1);
// これ覚えてから初期化が楽になった
Two Pointers テクニック
// 配列を反転
function reverse(arr) {
let left = 0;
let right = arr.length - 1;
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
}
面接で聞かれた配列の問題
- 配列内の重複を削除 → Setを使えば簡単だけど、in-placeでやるのは意外と難しい
- 配列を右にk個回転 → 3回反転で解ける(最初は思いつかなかった)
- 部分配列の最大和 → Kadane’s Algorithm(DPの入門)
まとめ:配列について今思うこと
- シンプルだけど奥が深い
- インデックス操作は慎重に(off-by-oneエラー多い)
- 時間計算量を意識すると良いコードが書ける
- 連結リストと比較すると、それぞれの良さがわかる
次は連結リストについてまとめる予定
Tags:
配列
基礎