leetcode146. LRU缓存机制
Posted SalvationN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode146. LRU缓存机制相关的知识,希望对你有一定的参考价值。
题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
示例:
输入
["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]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
思路
这题有几个需要注意的点:
- cache是有容量限制的
- 需要根据最近是否使用过来排定数据的顺序
- get和put都看做是一次访问
为了保证get和put都满足O(1)的时间复杂度,又由于是根据最近是否访问来决定淘汰顺序,最直观的方案是使用队列。但队列有个致命缺陷——只能操作队头元素和队尾元素,这就意味着get()操作时十分麻烦。
另一个经典的思路是双链表+hashmap,双链表的插入删除时间复杂度都是O(1),而hashmap指针取出结点指针保证了get()的时间复杂度为O(1)。
另外有几点需要注意:
- 双链表应设置头结点和尾结点,可以避免空链表的边界情况,省去很多麻烦
- get()算作一次访问,访问后需要让该结点重新置于链表的尾部(或头部,看怎么设置和理解,下面代码采用和循环链表同样的概念,即表尾进表头出,也就是表尾代表最新访问的结点,表头代表下一个移除的结点)。代码操作也十分容易,只需将结点在链表中移除(不需要delete),然后再插入到表尾即可
- 在超出缓存限制需要删除结点时,别忘了delete指针,避免内存泄漏。
代码
struct DLinkNode {
int key, value;
DLinkNode *prev, *next;
DLinkNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
public:
LRUCache(int capacity) {
this->cap = capacity;
this->len = 0;
tail = new DLinkNode;
head = new DLinkNode;
tail -> prev = head;
head -> next = tail;
}
int get(int key) {
if(hashmap.count(key)) {
DLinkNode* node = hashmap[key];
movetoTail(node);
return node -> value;
}
return -1;
}
void put(int key, int value) {
if(!hashmap.count(key)) {
DLinkNode* node = new DLinkNode(key, value);
addNode(node);
hashmap[key] = node;
len++;
if(len > cap) {
removeNode(head -> next);
len--;
}
}
else {
hashmap[key] -> value = value;
movetoTail(hashmap[key]);
}
}
void movetoTail(DLinkNode* node) {
node -> prev -> next = node -> next;
node -> next -> prev = node -> prev;
tail -> prev -> next = node;
node -> prev = tail -> prev;
tail -> prev = node;
node -> next = tail;
}
void addNode(DLinkNode* node) {
tail -> prev -> next = node;
node -> prev = tail -> prev;
node -> next = tail;
tail -> prev = node;
}
void removeNode(DLinkNode* node) {
node -> prev -> next = node -> next;
node -> next -> prev = node -> prev;
hashmap.erase(node->key);
delete node;
}
private:
unordered_map<int, DLinkNode*> hashmap;
DLinkNode *head, *tail;
int cap;
int len;
};
以上是关于leetcode146. LRU缓存机制的主要内容,如果未能解决你的问题,请参考以下文章