题目:
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该 逐出 最久未使用的关键字。
函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
示例:
输入 ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 输出 [null, null, null, 1, null, -1, null, -1, 3, 4]
题解:
哈希表+双向链表
题目要求实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。实际上就是当缓存满了的时候,需要继续存入数据时,从缓存中寻找最近最少使用的元素弹出,以存入新数据。
整体思路是通过双向链表来维护最近的访问顺序,把最近使用过的数据放到链表头,这样链表尾时始终是最近最少使用的数据,同时用哈希表来实现O(1)时间复杂度的查找(缓存数据的键映射到其在双向链表中的位置)。每次访问或插入数据时,都及时更新双向链表和哈希表,以满足LRU缓存的约束。
首先定义一个newListNode类,用于表示双向链表的节点。这个节点包含键值对以及指向前一个节点和后一个节点的指针。
在双向链表的实现中,使用一个 头部 head 和 尾部 tail 标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。
get方法用于获取给定key对应的value。如果key存在于缓存中,就将对应的节点移动到双向链表的头部(表示最近使用),并返回对应的value;如果key不存在,就返回-1。
put方法用于向缓存中插入key-value组。如果key已经存在于缓存中,就更新对应节点的value,并将节点移动到双向链表的头部;如果key不存在,就创建一个新的节点,并将节点加入到双向链表的头部,同时更新哈希表。如果插入操作导致关键字数量超过capacity,就移除最久没有使用的节点,并更新哈希表。
代码如下:
class newListNode {
public:
int key, value;
newListNode* prev;
newListNode* next;
newListNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
int capacity;
std::unordered_map<int, newListNode*> cache;
newListNode* head;
newListNode* tail;
//将节点结点添加到表头
void addToFront(newListNode* node) {
node->next = head->next;
node->prev = head;
head->next->prev = node;
head->next = node;
}
//将结点移除
void removeFromList(newListNode* node) {
newListNode* prev = node->prev;
newListNode* next = node->next;
prev->next = next;
next->prev = prev;
}
//将结点移动到表头
void moveToFront(newListNode* node) {
removeFromList(node);
addToFront(node);
}
//最近最少使用的结点
newListNode* removeLRU() {
newListNode* lru = tail->prev;
removeFromList(lru);
return lru;
}
public:
LRUCache(int _capacity) : capacity(_capacity) {
head = new newListNode(-1, -1);
tail = new newListNode(-1, -1);
head->next = tail;
tail->prev = head;
}
//获取给定key对应的value
int get(int key) {
if (cache.find(key) != cache.end()) {
newListNode* node = cache[key];
moveToFront(node);
return node->value;
}
return -1;
}
//向缓存中插入key-value组
void put(int key, int value) {
if (cache.find(key) != cache.end()) {
newListNode* node = cache[key];
node->value = value;
moveToFront(node);
} else {
if (cache.size() == capacity) {
newListNode* lru = removeLRU();
cache.erase(lru->key);
delete lru;
}
newListNode* newNode = new newListNode(key, value);
cache[key] = newNode;
addToFront(newNode);
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/