1、LRU
https://leetcode.cn/problems/lru-cache/
typedef pair<int, int> PII;
class LRUCache {
public:
int cap, cnt;
//pair<第几个插入的,val>
int l[200010], r[200010], h[200010], idx; //h用来通过key快速找到结点
PII e[200010];
//维护当前最后一个节点是第几个插入的
void insert(int k, PII x){
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
l[r[k]] = idx;
r[k] = idx++;
cnt++;
}
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
cnt--;
}
LRUCache(int capacity) {
this->cap = capacity;
memset(l, -1, sizeof l);
memset(r, -1, sizeof r);
memset(h, -1, sizeof h);
r[0] = 1, l[1] = 0; //0,1是哨兵
idx = 2;
cnt = 0;
}
//用头插法维护这个双链表!
int get(int key) {
if(h[key] == -1) return -1;
//把链表中老的点删除
PII t = e[h[key]];
remove(h[key]);
h[key] = idx;
insert(0, t); //会更新idx
return t.second;
}
void put(int key, int value) {
if(h[key] != -1) remove(h[key]);
insert(0, {key, value});
h[key] = idx - 1; //更新h[key]
if(cnt > cap){ //需要逐出
//删除h中
h[e[l[1]].first] = -1;
remove(l[1]);
}
}
};
/**
* 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);
*/
2、LFU
https://leetcode.cn/problems/lfu-cache/description/
//参考思路 https://leetcode.cn/problems/lfu-cache/solutions/977393/lru-lfu-by-mats9693-sy1x/
const int N = 200010;
class LFUCache {
public:
struct Node{
int key, value, freq;
};
int h[N], mp[N], l[N], r[N], idx, cap, n; //mp[]:用key索引idx
int ct[N]; //freq对应的结点的个数
Node e[N]; //e[]:用idx索引,
int pu;
LFUCache(int capacity) {
this->cap = capacity;
memset(h, -1, sizeof h);
memset(l, -1, sizeof l);
memset(r, -1, sizeof r);
memset(mp, -1, sizeof mp);
r[0] = 1, l[1] = 0, idx = 2;
pu = 0;
n = 0;
}
void insert(int k, Node x){
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
r[k] = idx;
l[r[idx]] = idx;
idx++;
n++;
}
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
n--;
}
int get(int key) {
if(mp[key] == -1) return -1;
auto t = e[mp[key]];
//如何知道当前结点右边的idx
int rr = r[mp[key]];
bool flag = false;
if(ct[t.freq + 1] > 0){ //插入到freq+1部分的最前面
//先删除掉freq的这个结点
remove(mp[key]);
int pos = h[t.freq + 1];
insert(l[pos], {t.key, t.value, t.freq + 1});
// insert(l[pos], {t.key, t.value, t.freq});
flag = true;
}
//没有freq+1的部分,但是有freq部分(不止自己一个),那么插入到freq部分的最前面
else if(ct[t.freq] > 1 && e[h[t.freq]].key != t.key){
remove(mp[key]);
int pos = h[t.freq];
insert(l[pos], {t.key, t.value, t.freq + 1});
// insert(l[pos], {t.key, t.value, t.freq});
flag = true;
}
//更新h
if(flag) h[t.freq + 1] = idx - 1, mp[key] = idx - 1; //因为更换了位置,所以mp也要更改
else {
h[t.freq + 1] = mp[key] = h[t.freq];
e[mp[key]].freq++; //这种情况也要++
}
if(ct[t.freq] <= 1){ //没有其他freq = n的结点
h[t.freq] = -1; //注意:这里key是不会消失的,只是ct变化了而已
}
else if(e[h[t.freq]].key == t.key){ //如果ct > 1且它原先是作为头部存在的
//错误想法:
// h[t.freq] = r[idx - 1];
// h[t.freq] = r[h[t.freq + 1]];
//**正确想法**: 要用它原先的右边!
h[t.freq] = rr;
}
ct[t.freq]--;
ct[t.freq + 1]++;
pu = mp[key];
// e[pu].freq++; //将缓存次数+1
return t.value;
}
void put(int key, int value) {
if(mp[key] != -1) {
get(key);
//map[key]可能已经更改了,所以下面注释代码是错误的
// e[mp[key]].value = value;
e[pu].value = value;
return;
}
//key不存在
if(n >= cap){ //逐出最后一个结点
auto t = e[l[1]];
remove(l[1]);
mp[t.key] = -1; //一定要把key清掉
if(--ct[t.freq] <= 0) h[t.freq] = -1;//如果由于逐出key导致ct->0,那么mp也要清除
}
//插入,此时freq = 1
if(ct[1] > 0){ //需要移动到freq=1部分的开头
int pos = h[1];
insert(l[pos], {key, value, 1});
}
else{ //插入到末尾
insert(l[1], {key, value, 1});
}
h[1] = idx - 1;
ct[1]++;
mp[key] = idx - 1;
}
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/