クロージャーパターン による O(1) 実装
整数
n
を受け取り、カウンター関数を返す高階関数を実装します。
返されたカウンター関数は、初回呼び出し時に
n
を返し、 2回目以降は前回の値より1大きい値を返します(n+1,
n+2, ...)。
入力: n = 10, ["call","call","call"]
出力: [10, 11, 12]
説明:
counter() = 10 // 初回呼び出し、n を返す
counter() = 11 // 1増加した値を返す
counter() = 12 // さらに1増加した値を返す
-1000 <= n <= 1000
0 <= calls.length <= 1000
calls[i] === "call"
n++
で現在値を返してから増加
時間計算量
O(1)
各呼び出しで定数時間
空間計算量
O(1)
単一の数値変数のみ
/**
* カウンター関数を生成する高階関数
*
* @param n - カウンターの初期値 (-1000 <= n <= 1000)
* @returns 呼び出すたびにインクリメントされる値を返す関数
* @throws {RangeError} n が制約範囲外の場合
* @throws {TypeError} n が有限数でない場合
*
* @complexity
* - Time: O(1) for creation and each call
* - Space: O(1) per counter instance
*
* @example
* const counter = createCounter(10);
* counter(); // 10
* counter(); // 11
* counter(); // 12
*/
function createCounter(n: number): () => number {
// 入力検証: 制約条件チェック
if (n < -1000 || n > 1000) {
throw new RangeError(
`Initial value ${n} is out of bounds [-1000, 1000]`
);
}
// 型ガード: number型の確認
if (typeof n !== 'number' || !Number.isFinite(n)) {
throw new TypeError(
'Initial value must be a finite number'
);
}
/**
* カウンター関数(クロージャー)
*
* クロージャースコープ内の変数 n を保持し、
* 呼び出すたびに現在値を返してから1増加させる
*
* @returns 現在のカウント値
*
* @invariant n は常に整数値を保持
* @invariant k回目の呼び出しは (初期値 + k - 1) を返す
*/
return function(): number {
// 後置インクリメント演算子:
// 1. 現在の n の値を評価(返却用)
// 2. n に 1 を加算(次回呼び出し用)
// 3. ステップ1の値を return
return n++;
};
}
// LeetCode 最小提出版
function createCounter(n: number): () => number {
return function(): number {
return n++;
};
}
フローの説明:
1. 入力検証: n
が制約範囲内かチェック(-1000 ≤ n ≤ 1000)
2. エラー処理: 範囲外の場合は
RangeError をスロー
3. クロージャー作成: 変数 n
をレキシカルスコープに保持した内部関数を生成
4. 関数返却:
カウンター関数を呼び出し元に返す
5. counter() 呼び出し:
クロージャー内の n にアクセス
6. 後置インクリメント: 現在の n
を評価してから 1 を加算
7. 値を返却: 元の n の値を返す
8. ループバック:
次回呼び出し時は更新された n で再度実行
O(1)
O(1)
| アプローチ | 時間 | 空間 | 型安全性 | 実装複雑度 |
|---|---|---|---|---|
| クロージャー + n++ | O(1) | O(1) | 高 | 最低 |
| クロージャー + カウント変数 | O(1) | O(1) | 高 | 低 |
| Class ベース | O(1) | O(1) | 高 | 中 |
| Generator 関数 | O(1) | O(1) | 中 | 中 |
推奨: クロージャー +
n++
が最もシンプルで LeetCode の期待解