C++简单实现LRU缓存结构
简单了解一下(个人解释)
LRU缓存结构:
有一个池子,有一堆数据,池子的容纳上限是k,
有两种操作
- set:放数据(如果超出上限了,删除最晚访问的那条数据)
- get:访问数据
这里有个要求:
get、set的时间复杂度都需要是O(1)
推理过程:
- 先考虑一下我们需要什么样的功能
- 访问时候为1,插入删除时为1,这样的话,没有一个数据结构是可以同时符合这种特征的
- 但是我们可以将两种结构结合在一起,达到需要的效果
- 使用链表方便插入和删除,使用map解决链表的访问问题。
设计实现:
- 使用map记录每个节点的key值和地址
- 访问的时候通过map的key值直接访问链表中的某个节点
- 因为map记录的是当前节点的地址,所以单向链表并不能很好的达到我们的要求,所以维护的是一个双向链表
- 其实第二张图并不是很好,因为头和尾应该是初始就存在的节点,就像第一张图那样,这样会比较方便
具体流程(见代码注释):
class Solution {
public:
struct node {//链表的每个节点
node* next;
node* pre;
int key;
int val;
node(int k, int v) :key(k), val(v), next(nullptr), pre(nullptr) {}
};
node* head = new node(-1, -1);//定义头结点
node* tail = new node(-1, -1);//定义尾节点
int cnt = 0;//记录链表中数据的数量
int maxlen = 0;//记录数据最大上限
map<int, node* >mp;//map
Solution() {//初始化,首尾相连
head->next = tail;
tail->next = head;
}
//将访问的节点放在第一个位置
void update(node* p) {
//将p节点从原位置删除
node* q = p->pre;
q->next = p->next;
p->next->pre = q;
//将p节点插入到第一个位置
p->next = head->next;
head->next->pre = p;
head->next = p;
p->pre = head;
}
//插入数据
void set(int key, int val) {
if (mp.count(key)) {//if存在,更新p节点的位置
update(mp[key]);
}
else {//else,更新数据
//创建新节点,更新map和cnt
node* p = new node(key, val);
mp[key] = p;
cnt++;
//将p节点插入到第一个位置
p->next = head->next;
p->next->pre = p;
head->next = p;
p->pre = head;
//if数据超出限制了,删除最后一个节点
if (cnt > maxlen) {
node* q = tail->pre->pre;
node* t = q->next;
q->next = tail;
tail->pre = q;
mp.erase(t->key);//删除map里面的数据
delete t;//释放内存,可以没有,建议有
}
}
}
//访问数据
int get(int key) {
//if有数据,更新节点,并返回
if (mp.count(key)) {
node* p = mp[key];
update(p);
return p->val;
}
else {//返回-1
node* p = new node(-1, -1);
return p->val;
}
}
vector<int> LRU(vector<vector<int> >& operators, int k) {
maxlen = k;
vector<int>ans;
for (auto x : operators) {
if (x[0] == 1) {
set(x[1], x[2]);
}
else if (x[0] == 2) {
ans.emplace_back(get(x[1]));
}
}
return ans;
}
};