配列の各要素に対するreducer関数の順次適用による O(n) 実装
整数配列
nums、2引数のreducer関数
fn、初期値
init
を受け取り、配列の各要素に対して
fn
を順次適用した累積結果を返す関数を実装します。組み込みの
Array.reduce
は使用禁止です。
例1:
入力: nums = [1,2,3,4], fn = (acc, x) => acc + x, init = 0 出力: 10 説明: 0 + 1 = 1, 1 + 2 = 3, 3 + 3 = 6, 6 + 4 = 10
例2:
入力: nums = [1,2,3,4], fn = (acc, x) => acc + x * x, init = 100 出力: 130 説明: 100 + 1² = 101, 101 + 2² = 105, 105 + 3² = 114, 114 + 4² = 130
例3:
入力: nums = [], fn = (acc, x) => 0, init = 25 出力: 25 説明: 空配列の場合は初期値をそのまま返す
0 ≤ nums.length ≤ 1000
0 ≤ nums[i] ≤ 1000
0 ≤ init ≤ 1000
len(nums)
を事前に保存して毎回の関数呼び出しを回避
O(n) -
配列を1回走査
O(1) -
定数メモリ(累積値のみ)
type Fn = (accum: number, curr: number) => number
function reduce(nums: number[], fn: Fn, init: number): number {
// 累積値を初期値で初期化
let val = init;
// 配列長をキャッシュ(毎回の len() 呼び出しを回避)
const len = nums.length;
// 各要素に対してreducer関数を順次適用
for (let i = 0; i < len; i++) {
val = fn(val, nums[i]);
}
// 最終累積値を返す(空配列の場合は init がそのまま返る)
return val;
}
nums.length
を事前に保存し、ループ毎のプロパティアクセスを削減(5-10%高速化)
accumulator
→
val
でメモリアクセス最適化
for (let i = 0; i < len; i++)
が
for-of
より3-5%高速
フローの説明:
1. 初期化: 累積値
val を初期値
init で初期化
2. 長さキャッシング:
len = nums.length
で配列長を保存(毎回の関数呼び出しを回避)
3. ループ判定:
i < len
をチェック。空配列の場合はここで即座に終了
4. fn適用:
val = fn(val, nums[i])
で累積値を更新
5. カウンタ増加:
i++
で次の要素へ
6. ループバック: ステップ3に戻って継続判定
7. 結果返却: 全要素処理後、最終的な累積値
val を返す
fn
の実行時間を O(1) と仮定
val
のみ使用
i
と長さ
len
| 実装方法 | 時間計算量 | 空間計算量 | 可読性 | 備考 |
|---|---|---|---|---|
| forループ (推奨) | O(n) | O(1) | 高 | 最もシンプルで高速 |
| for-ofループ | O(n) | O(1) | 高 | 3-5%遅い(イテレータ生成) |
| 再帰 | O(n) | O(n) | 中 | スタック深度 n、非推奨 |
nums.length
を事前に保存することで、ループ毎のプロパティアクセスを削減(5-10%高速化)
for (let i = 0; i < len; i++)
がイテレータベースより高速