LRU 缓存是一种缓存淘汰策略,当缓存满时,会优先淘汰最久未使用的数据。
原理
LRU 缓存的核心思想是利用数据的访问顺序来决定哪些数据应该被淘汰。当访问一个数据时,将其标记为最近使用的数据;当缓存满时,淘汰最久未使用的数据。
实现思路
为了高效地实现 LRU 缓存,可以使用哈希表(std::unordered_map
)和双向链表(std::list
)结合的方式。
- 哈希表:用于快速查找某个键是否存在于缓存中,以及获取其对应的值。
- 双向链表:用于维护数据的访问顺序,链表头部表示最近使用的数据,链表尾部表示最久未使用的数据。
代码示例
#include <iostream>
#include <unordered_map>
#include <list>
class LRUCache {
private:
int capacity;
std::list<std::pair<int, int>> cacheList;
std::unordered_map<int, std::list<std::pair<int, int>>::iterator> cacheMap;
public:
LRUCache(int capacity) : capacity(capacity) {}
int get(int key) {
auto it = cacheMap.find(key);
if (it == cacheMap.end()) {
return -1;
}
// 将该键值对移到链表头部
auto node = *it->second;
cacheList.erase(it->second);
cacheList.push_front(node);
cacheMap[key] = cacheList.begin();
return node.second;
}
void put(int key, int value) {
auto it = cacheMap.find(key);
if (it != cacheMap.end()) {
// 若键已存在,更新值并移到链表头部
cacheList.erase(it->second);
} else if (cacheList.size() == capacity) {
// 若缓存已满,删除链表尾部元素
int lastKey = cacheList.back().first;
cacheMap.erase(lastKey);
cacheList.pop_back();
}
// 插入新的键值对到链表头部
cacheList.push_front({key, value});
cacheMap[key] = cacheList.begin();
}
};
int main() {
LRUCache cache(2);
cache.put(1, 1);
cache.put(2, 2);
std::cout << cache.get(1) << std::endl; // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
std::cout << cache.get(2) << std::endl; // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
std::cout << cache.get(1) << std::endl; // 返回 -1 (未找到)
std::cout << cache.get(3) << std::endl; // 返回 3
std::cout << cache.get(4) << std::endl; // 返回 4
return 0;
}
复杂度分析
时间复杂度:get
和 put
操作的时间复杂度均为 O(1),因为哈希表的查找和插入操作以及双向链表的插入和删除操作的时间复杂度都是 O(1)。
空间复杂度:O(capacity),主要用于存储哈希表和双向链表。