当前位置: 面试刷题>> LFU缓存 (经典算法题500道)


### 完整题目描述 **LFU(Least Frequently Used)缓存** 是一种缓存淘汰策略,它会跟踪每个数据项被访问的频率,并淘汰访问频率最低的数据项。当缓存达到其容量限制时,且需要添加新的数据项到缓存中时,LFU缓存会移除访问频率最低的数据项(如果有多个数据项具有相同的最低频率,则通常选择最久未被访问的那个)。 **题目要求**: 实现一个LFU缓存,该缓存应支持以下操作: 1. `get(key)`: 获取键 `key` 对应的值,如果键不存在则返回 -1。 2. `put(key, value)`: 插入或更新键 `key` 的值 `value`。如果缓存达到其容量限制,则应该在使用 `put` 方法之前,移除最不常用的数据项。 缓存的容量由构造函数的参数给定。 **示例**: ``` LFUCache cache = new LFUCache(2); // 缓存容量为 2 cache.put(1, 1); cache.put(2, 2); cache.get(1); // 返回 1 cache.put(3, 3); // 淘汰键 2 cache.get(2); // 返回 -1 (未找到) cache.get(3); // 返回 3 cache.put(4, 4); // 淘汰键 1 cache.get(1); // 返回 -1 (未找到) cache.get(3); // 返回 3 cache.get(4); // 返回 4 ``` ### PHP 示例代码 ```php class Node { public $key; public $value; public $freq; public $prev; public $next; function __construct($key, $value, $freq = 1) { $this->key = $key; $this->value = $value; $this->freq = $freq; $this->prev = null; $this->next = null; } } class LFUCache { private $capacity; private $minFreq; private $freqMap; private $keyMap; function __construct($capacity) { $this->capacity = $capacity; $this->minFreq = 0; $this->freqMap = []; $this->keyMap = []; } // 辅助函数:增加节点频率 private function increaseFreq($node) { $freq = $node->freq; $node->freq++; $this->removeNodeFromDLL($node, $this->freqMap[$freq]); if (!isset($this->freqMap[$node->freq])) { $this->freqMap[$node->freq] = new DoublyLinkedList(); if ($this->minFreq == $freq) { $this->minFreq++; } } $this->freqMap[$node->freq]->addToHead($node); } // 辅助函数:从双向链表中移除节点 private function removeNodeFromDLL($node, $dll) { if ($dll->head == $node) { $dll->head = $node->next; } if ($dll->tail == $node) { $dll->tail = $node->prev; } if ($node->prev !== null) { $node->prev->next = $node->next; } if ($node->next !== null) { $node->next->prev = $node->prev; } } // 其他函数如 get, put, DoublyLinkedList 定义略 } // 注意:这里省略了 DoublyLinkedList 的定义和 put, get 方法的具体实现, // 因为它们需要更详细的逻辑来处理双向链表和哈希表的结合使用。 ``` ### Python 示例代码 Python 示例将更为简洁,使用标准库 `collections` 中的 `OrderedDict` 来简化实现。 ```python from collections import OrderedDict class LFUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = OrderedDict() self.freq_map = {} def get(self, key: int) -> int: if key not in self.cache: return -1 freq = self.cache[key][1] self.cache.move_to_end(key) # 将访问的键移动到有序字典的末尾 # 更新频率 if freq in self.freq_map: self.freq_map[freq].remove(key) if not self.freq_map[freq]: del self.freq_map[freq] freq += 1 if freq not in self.freq_map: self.freq_map[freq] = [] self.freq_map[freq].append(key) self.cache[key] = (self.cache[key][0], freq) return self.cache[key][0] def put(self, key: int, value: int) -> None: if self.capacity <= 0: return if key in self.cache: self.cache.move_to_end(key) _, freq = self.cache[key] self.freq_map[freq].remove(key) if not self.freq_map[freq]: del self.freq_map[freq] freq += 1 else: if len(self.cache) >= self.capacity: min_freq = min(self.freq_map.keys()) evict_key = self.freq_map[min_freq].pop(0) del self.cache[evict_key] if not self.freq_map[min_freq]: del self.freq_map[min_freq] freq = 1 if freq not in self.freq_map: self.freq_map[freq] = [] self.freq_map[freq].append(key) self.cache[key] = (value, freq) # 示例用法略 ``` ### JavaScript 示例代码 JavaScript 示例将使用 Map 和数组来实现类似的功能。 ```javascript class LFUCache { constructor(capacity) { this.capacity = capacity; this.cache = new Map(); this.freqMap = new Map(); this.minFreq = 0; } get(key) { if (!this.cache.has(key)) return -1; const [value, freq] = this.cache.get(key); this.cache.delete(key); if (this.freqMap.has(freq)) { const list = this.freqMap.get(freq); const index = list.indexOf(key); list.splice(index, 1); if (list.length === 0) this.freqMap.delete(freq); if (freq === this.minFreq && this.freqMap.get(freq) === undefined) { this.minFreq++; } } const newFreq = freq + 1; if (!this.freqMap.has(newFreq)) { this.freqMap.set(newFreq, []); } this.freqMap.get(newFreq).push(key); this.cache.set(key, [value, newFreq]); return value; } put(key, value) { if (this.capacity <= 0) return; if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size === this.capacity) { const minFreqKeys = this.freqMap.get(this.minFreq); const evictKey = minFreqKeys.shift(); this.cache.delete(evictKey); if (minFreqKeys.length === 0) { this.freqMap.delete(this.minFreq); this.minFreq = 0; for (let [freq, keys] of this.freqMap) { this.minFreq = freq; break; } } } const freq = this.cache.has(key) ? this.cache.get(key)[1] + 1 : 1; if (!this.freqMap.has(freq)) { this.freqMap.set(freq, []); } this.freqMap.get(freq).push(key); if (freq ===
推荐面试题