アルゴリズム概要

問題の説明

整数 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)

単一の数値変数のみ

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

TypeScript実装

/**
 * カウンター関数を生成する高階関数
 *
 * @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++;
    };
}

フローチャート

開始 createCounter(n) 入力検証 範囲チェック いいえ エラー RangeError はい クロージャー作成 変数nをスコープに保持 内部関数を返却 カウンター関数返却 counter() 呼び出し クロージャー内のnにアクセス n++ 実行 1. 現在のnを評価 2. nに1を加算 元の値を返却 次回呼び出しへ

フローの説明:
1. 入力検証: n が制約範囲内かチェック(-1000 ≤ n ≤ 1000)
2. エラー処理: 範囲外の場合は RangeError をスロー
3. クロージャー作成: 変数 n をレキシカルスコープに保持した内部関数を生成
4. 関数返却: カウンター関数を呼び出し元に返す
5. counter() 呼び出し: クロージャー内の n にアクセス
6. 後置インクリメント: 現在の n を評価してから 1 を加算
7. 値を返却: 元の n の値を返す
8. ループバック: 次回呼び出し時は更新された n で再度実行

計算量分析

時間計算量

O(1)

  • createCounter: O(1) - 入力検証とクロージャー作成
  • counter(): O(1) - 後置インクリメント演算(CPU命令1つ)

空間計算量

O(1)

  • 補助空間: なし
  • クロージャー変数: 8バイト(number型)× カウンター数

アプローチ比較

アプローチ 時間 空間 型安全性 実装複雑度
クロージャー + n++ O(1) O(1) 最低
クロージャー + カウント変数 O(1) O(1)
Class ベース O(1) O(1)
Generator 関数 O(1) O(1)

推奨: クロージャー + n++ が最もシンプルで LeetCode の期待解