问题描述
以146. LRU为例,题目如下:
思路
可以定义链表节点对应缓冲块,这样就可以用双向链表保存当前的缓存块,每次加入、更新或查找缓存块都会使对应节点移动至链表头,每次需要替换出缓存块时将链尾节点(最久未使用)移除,使用链表的好处是定位到对应节点后,再进行插入或删除操作的时间复杂度都是 O ( 1 ) O(1) O(1)的,但定位链表节点就比较慢,所有可以使用哈希表记录缓存块的键对应的链表节点,这样定位链表节点的时间复杂度就是近似 O ( 1 ) O(1) O(1)的。在操作过程我们维护哈希表和链表(链表头尾节点),这样就可以实现O(1)的平均时间复杂度的 g e t get get和 p u t put put方法。实现过程中可以封装两个基本的方法:1) e m p l a c e _ f r o n t emplace\_front emplace_front: 将新节点插入链表头同时更新哈希表, 2) u p d a t e update update: 更新或访问链表中的节点:先将原节点从链表中删除,再将新节点插入链表头,同时更新哈希表node。
实现代码
版本1. 自定义链表节点实现
双向链表节点类:
class Node {
public:
int k, v;//键,值
Node *prev, *next;//前驱节点,后继节点
Node(int key = -1, int val = -1, Node *prev_ = nullptr, Node *next_ = nullptr) : k(key), v(val), prev(prev_), next(next_) {}
};
LRU实现:
class LRUCache {
public:
LRUCache(int capacity) {
n = capacity;
cnt = 0;
head = new Node();
tail = head;
}
int get(int key) {
if (!node.count(key))
return -1;
update(key);
return node[key]->v;
}
void put(int key, int value) {
if (node.count(key)) {
update(key, value);
return;
}
if (cnt++ == n) {
cnt--;
node.erase(tail->k);
Node *tail0 = tail;
tail = tail->prev;
tail->next = nullptr;
delete tail0;
}
emplace_front(key, value);
}
private:
int n;
Node *head, *tail;//链表头尾节点
unordered_map<int, Node *> node;//键到链表节点的映射
int cnt;//节点数
void emplace_front(int key, int value) {//将(key,value)插入链表头, 同时更新哈希表node
Node *t = new Node(key, value, head, head->next);
if (head == tail)//可能需要更新tail节点
tail = t;
if (head->next)
head->next->prev = t;
head->next = t;
node[key] = t;//更新哈希表
}
void update(int key, int val = -1) {//更新或访问(key,val): 先将(key,val)从链表中删除, 再将新的(key,val)插入链表头, 同时更新哈希表node
Node *x = node[key];
if (val != -1)//更新val
x->v = val;
if (x == tail)
tail = tail->prev;
x->prev->next = x->next;
if (x->next)
x->next->prev = x->prev;
emplace_front(x->k, x->v);
delete x;
}
};
版本2. list迭代器模拟链表节点实现
使用list同时用list的迭代器模拟链表节点,这样的好处就是不用手动维护链表的尾节点,同时list有 e m p l a c e _ f r o n t emplace\_front emplace_front、 p o p _ b a c k pop\_back pop_back等方法,就不用再去实现一遍,所有通过list迭代器模拟链表节点来实现比自定义链表节点实现会更简洁一些。
class LRUCache {
public:
LRUCache(int capacity) {
n = capacity;
}
int get(int key) {
if (!node.count(key))
return -1;
update(key);
return node[key]->second;
}
void put(int key, int value) {
if (node.count(key)) {
update(key, value);
return;
}
if (li.size() == n) {
node.erase(li.rbegin()->first);
li.pop_back();
}
li.emplace_front(key, value);
node[key] = li.begin();
}
private:
int n;
list<pair<int, int>> li;//链表
unordered_map<int, list<pair<int, int>>::iterator> node;//键到链表节点的映射
void update(int key, int val = -1) {//更新或访问(key,val): 先将(key,val)从链表中删除, 再将新的(key,val)插入链表头, 同时更新哈希表node
auto x = *node[key];
if (val != -1)
x.second = val;
li.erase(node[key]);
li.push_front(x);
node[key] = li.begin();
}
};