遅延削除方式による O(1) set/get 実装
有効期限付きキャッシュクラスを実装します。各キーに有効期限(ミリ秒)を設定し、期限切れのキーは自動的にアクセス不可になります。
3つのメソッドを実装する必要があります:
set(key, value, duration): キーと値を設定。既存の未期限切れキーがあれば true、なければ false
を返す
get(key):
未期限切れキーの値を返す。存在しない、または期限切れなら -1
count():
未期限切れキーの総数を返す
Input:
actions = ["TimeLimitedCache", "set", "get", "count", "get"]
values = [[], [1, 42, 100], [1], [], [1]]
timeDelays = [0, 0, 50, 50, 150]
Output: [null, false, 42, 1, -1]
説明:
t=0: キャッシュを構築
t=0: set(1, 42, 100) → false(新規キー)
t=50: get(1) → 42(未期限切れ)
t=50: count() → 1(アクティブなキー)
t=100: key=1 が期限切れ
t=150: get(1) → -1(期限切れ)
遅延削除方式(Lazy Deletion)を採用します:
setTimeout
を使わず、Date.now()
との比較で期限判定
get
時に期限切れなら遅延削除
count
時に全エントリを走査して有効数をカウント
/**
* キャッシュエントリの内部構造
* @property value - 保存された値
* @property expiresAt - 期限時刻(ミリ秒、Date.now()ベース)
*/
interface CacheEntry {
value: number;
expiresAt: number;
}
/**
* 有効期限付きキャッシュクラス(遅延削除方式)
* @description タイマーを使わず期限時刻で管理することで高速化
*/
class TimeLimitedCache {
private cache: Map<number, CacheEntry>;
constructor() {
this.cache = new Map<number, CacheEntry>();
}
/**
* キーと値を設定し、有効期限を指定
* @param key - キー (0 <= key <= 10^9)
* @param value - 値 (0 <= value <= 10^9)
* @param duration - 有効期限(ミリ秒、0 <= duration <= 1000)
* @returns 既存の未期限切れキーが存在した場合true、それ以外false
* @complexity Time: O(1), Space: O(1)
*/
set(key: number, value: number, duration: number): boolean {
// 現在時刻と期限時刻を計算
const now = Date.now();
const expiresAt = now + duration;
// 既存エントリの確認
const existingEntry = this.cache.get(key);
// 既存エントリが存在し、かつ未期限切れかチェック
const hadUnexpiredKey = existingEntry !== undefined
&& existingEntry.expiresAt > now;
// 新しいエントリを設定(タイマー不要)
this.cache.set(key, { value, expiresAt });
return hadUnexpiredKey;
}
/**
* キーに対応する値を取得
* @param key - 取得するキー
* @returns 未期限切れのキーが存在すれば対応する値、存在しなければ-1
* @complexity Time: O(1), Space: O(1)
*/
get(key: number): number {
const entry = this.cache.get(key);
// エントリが存在しない場合
if (entry === undefined) {
return -1;
}
// 期限切れチェック
if (entry.expiresAt <= Date.now()) {
// 遅延削除: get時に初めて削除
this.cache.delete(key);
return -1;
}
return entry.value;
}
/**
* 未期限切れキーの数を取得
* @returns アクティブなキーの数
* @complexity Time: O(n), Space: O(1)
*/
count(): number {
const now = Date.now();
let count = 0;
// 全エントリを走査して有効なもののみカウント
for (const entry of this.cache.values()) {
if (entry.expiresAt > now) {
count++;
}
}
return count;
}
}
/**
* 使用例:
* const timeLimitedCache = new TimeLimitedCache()
* timeLimitedCache.set(1, 42, 1000); // false
* timeLimitedCache.get(1) // 42
* timeLimitedCache.count() // 1
*/
フローの説明:
1. 期限時刻を計算:
Date.now() + duration
で期限時刻を算出
2. 既存エントリのチェック: Mapから既存エントリを取得
3. 期限切れ判定: 既存エントリがある場合、expiresAt > now
で有効性を確認
4. フラグ設定: 未期限切れなら true、それ以外は false
5. エントリ更新: 新しい値と期限時刻でMapを更新
6. 結果を返却: hadUnexpiredKey を返す
| メソッド | 時間計算量 | 空間計算量 | 理由 |
|---|---|---|---|
| set | O(1) | O(1) | Map操作 + 期限時刻計算のみ |
| get | O(1) | O(1) | Map取得 + 期限判定 + 削除(最悪) |
| count | O(n) | O(1) | 全エントリ走査(n = 現在のキー数) |
| 方式 | set | get | count | メモリ | 備考 |
|---|---|---|---|---|---|
| setTimeout方式 | O(1) + タイマー | O(1) | O(1) | 中 | タイマーオーバーヘッド |
| 遅延削除方式 ★ | O(1) | O(1) | O(n) | 軽 | タイマー不要で高速 |
| 積極削除方式 | O(1) | O(1) | O(n) | 最軽 | count時に一括削除 |