LRU&LFU实现方式C++

yuanheci 2023年09月25日 628次浏览

参考文章

1、LRU

https://leetcode.cn/problems/lru-cache/

  1. typedef pair<int, int> PII;
  2. class LRUCache {
  3. public:
  4. int cap, cnt;
  5. //pair<第几个插入的,val>
  6. int l[200010], r[200010], h[200010], idx; //h用来通过key快速找到结点
  7. PII e[200010];
  8. //维护当前最后一个节点是第几个插入的

  9. void insert(int k, PII x){
  10. e[idx] = x;
  11. l[idx] = k;
  12. r[idx] = r[k];
  13. l[r[k]] = idx;
  14. r[k] = idx++;
  15. cnt++;
  16. }

  17. void remove(int k){
  18. r[l[k]] = r[k];
  19. l[r[k]] = l[k];
  20. cnt--;
  21. }

  22. LRUCache(int capacity) {
  23. this->cap = capacity;
  24. memset(l, -1, sizeof l);
  25. memset(r, -1, sizeof r);
  26. memset(h, -1, sizeof h);
  27. r[0] = 1, l[1] = 0; //0,1是哨兵
  28. idx = 2;
  29. cnt = 0;
  30. }

  31. //用头插法维护这个双链表!
  32. int get(int key) {
  33. if(h[key] == -1) return -1;
  34. //把链表中老的点删除
  35. PII t = e[h[key]];
  36. remove(h[key]);
  37. h[key] = idx;
  38. insert(0, t); //会更新idx
  39. return t.second;
  40. }

  41. void put(int key, int value) {
  42. if(h[key] != -1) remove(h[key]);
  43. insert(0, {key, value});
  44. h[key] = idx - 1; //更新h[key]
  45. if(cnt > cap){ //需要逐出
  46. //删除h中
  47. h[e[l[1]].first] = -1;
  48. remove(l[1]);
  49. }
  50. }
  51. };

  52. /**
  53. * Your LRUCache object will be instantiated and called as such:
  54. * LRUCache* obj = new LRUCache(capacity);
  55. * int param_1 = obj->get(key);
  56. * obj->put(key,value);
  57. */

2、LFU

https://leetcode.cn/problems/lfu-cache/description/

  1. //参考思路 https://leetcode.cn/problems/lfu-cache/solutions/977393/lru-lfu-by-mats9693-sy1x/

  2. const int N = 200010;
  3. class LFUCache {
  4. public:
  5. struct Node{
  6. int key, value, freq;
  7. };

  8. int h[N], mp[N], l[N], r[N], idx, cap, n; //mp[]:用key索引idx
  9. int ct[N]; //freq对应的结点的个数
  10. Node e[N]; //e[]:用idx索引,
  11. int pu;

  12. LFUCache(int capacity) {
  13. this->cap = capacity;
  14. memset(h, -1, sizeof h);
  15. memset(l, -1, sizeof l);
  16. memset(r, -1, sizeof r);
  17. memset(mp, -1, sizeof mp);
  18. r[0] = 1, l[1] = 0, idx = 2;
  19. pu = 0;
  20. n = 0;
  21. }

  22. void insert(int k, Node x){
  23. e[idx] = x;
  24. r[idx] = r[k];
  25. l[idx] = k;
  26. r[k] = idx;
  27. l[r[idx]] = idx;
  28. idx++;
  29. n++;
  30. }

  31. void remove(int k){
  32. r[l[k]] = r[k];
  33. l[r[k]] = l[k];
  34. n--;
  35. }

  36. int get(int key) {
  37. if(mp[key] == -1) return -1;
  38. auto t = e[mp[key]];
  39. //如何知道当前结点右边的idx
  40. int rr = r[mp[key]];
  41. bool flag = false;
  42. if(ct[t.freq + 1] > 0){ //插入到freq+1部分的最前面
  43. //先删除掉freq的这个结点
  44. remove(mp[key]);
  45. int pos = h[t.freq + 1];
  46. insert(l[pos], {t.key, t.value, t.freq + 1});
  47. // insert(l[pos], {t.key, t.value, t.freq});
  48. flag = true;
  49. }
  50. //没有freq+1的部分,但是有freq部分(不止自己一个),那么插入到freq部分的最前面
  51. else if(ct[t.freq] > 1 && e[h[t.freq]].key != t.key){
  52. remove(mp[key]);
  53. int pos = h[t.freq];
  54. insert(l[pos], {t.key, t.value, t.freq + 1});
  55. // insert(l[pos], {t.key, t.value, t.freq});
  56. flag = true;
  57. }
  58. //更新h
  59. if(flag) h[t.freq + 1] = idx - 1, mp[key] = idx - 1; //因为更换了位置,所以mp也要更改
  60. else {
  61. h[t.freq + 1] = mp[key] = h[t.freq];
  62. e[mp[key]].freq++; //这种情况也要++
  63. }

  64. if(ct[t.freq] <= 1){ //没有其他freq = n的结点
  65. h[t.freq] = -1; //注意:这里key是不会消失的,只是ct变化了而已
  66. }
  67. else if(e[h[t.freq]].key == t.key){ //如果ct > 1且它原先是作为头部存在的
  68. //错误想法:
  69. // h[t.freq] = r[idx - 1];
  70. // h[t.freq] = r[h[t.freq + 1]];
  71. //**正确想法**: 要用它原先的右边!
  72. h[t.freq] = rr;
  73. }
  74. ct[t.freq]--;
  75. ct[t.freq + 1]++;
  76. pu = mp[key];
  77. // e[pu].freq++; //将缓存次数+1
  78. return t.value;
  79. }

  80. void put(int key, int value) {
  81. if(mp[key] != -1) {
  82. get(key);
  83. //map[key]可能已经更改了,所以下面注释代码是错误的
  84. // e[mp[key]].value = value;
  85. e[pu].value = value;
  86. return;
  87. }
  88. //key不存在
  89. if(n >= cap){ //逐出最后一个结点
  90. auto t = e[l[1]];
  91. remove(l[1]);
  92. mp[t.key] = -1; //一定要把key清掉
  93. if(--ct[t.freq] <= 0) h[t.freq] = -1;//如果由于逐出key导致ct->0,那么mp也要清除
  94. }
  95. //插入,此时freq = 1
  96. if(ct[1] > 0){ //需要移动到freq=1部分的开头
  97. int pos = h[1];
  98. insert(l[pos], {key, value, 1});
  99. }
  100. else{ //插入到末尾
  101. insert(l[1], {key, value, 1});
  102. }
  103. h[1] = idx - 1;
  104. ct[1]++;
  105. mp[key] = idx - 1;
  106. }
  107. };

  108. /**
  109. * Your LFUCache object will be instantiated and called as such:
  110. * LFUCache* obj = new LFUCache(capacity);
  111. * int param_1 = obj->get(key);
  112. * obj->put(key,value);
  113. */