LRU缓存是一种满足最近最少使用约束的数据结构。我们可以用一个简单的例子来理解:假设你有一摞书,最多只能放capacity本。当你需要找一本书时,如果书在摞中,就返回它的版本(即key-value);如果不在,就返回-1。当你想放入一本新书时,如果这本书已经存在,就更新它的版本号;如果不存在,就把新书放在最上面。如果书的数量超过了capacity,就把最下面那本书移出。
那么,在这个例子中,我们主要用到了哪些操作呢?又该用什么数据结构来实现呢?由于题目要求get()和put()的时间复杂度为O(1),并且需要同时存放key-value,还要删除最久未使用的元素,因此可以使用双向链表来解决。具体来说,我们主要用到了以下操作:
1.删除
将一个节点删除
2.将节点放在最前面
3.快速找出要找的节点
使用哈希表,用key与节点作映射
class Node {
public:
int key;
int value;
Node *prev;
Node *next;
Node(int k = 0,int v = 0):key(k),value(v){};
};
class LRUCache {
public:
int capacity = 0;
Node *dummy;
unordered_map<int,Node*> key_to_node;
//删除节点
void remove(Node *x) {
x->prev->next = x->next;
x->next->prev = x->prev;
}
//将节点放在最前
void push_front(Node *x) {
x->prev = dummy;
x->next = dummy->next;
x->next->prev = x;
x->prev->next = x;
}
//获取节点
Node* getNode(int key) {
auto it = key_to_node.find(key);
if (it == key_to_node.end()) {
return nullptr;
}
Node *node = key_to_node[key];
remove(node);
push_front(node);
return node;
}
LRUCache(int capacity) : capacity(capacity),dummy(new Node()) {
dummy->next = dummy;
dummy->prev = dummy;
}
int get(int key) {
Node *node = getNode(key);
return node ? node->value : -1;
}
void put(int key, int value) {
Node *node = getNode(key);
if (node) {
node->value = value;
return;
}
key_to_node[key] = node = new Node(key,value);
push_front(node);
if (capacity < key_to_node.size()) {
//最久未使用节点
Node *back_node = dummy->prev;
key_to_node.erase(back_node->key);
remove(back_node);
//释放内存
delete back_node;
}
}
};
时间复杂度:O(1)
空间复杂度:O(min(p,capacity),p为put的次数