当前位置:   article > 正文

【leetcode】缓存淘汰策略题目总结

【leetcode】缓存淘汰策略题目总结

146. LRU 缓存

我们使用了一个双向链表cache来存储数据,同时使用一个哈希表hash_map来映射键和链表节点的迭代器。当调用get(key)函数时,我们首先检查hash_map中是否存在该key,如果存在则将之前位置移到链表头部并返回值;当调用put(key, value)函数时,我们先检查hash_map中是否存在该key,存在的话将该节点从链表中删除,不管存在与否都需要考虑是否需要删除旧数据(缓存已满的情况)。

  1. class LRUCache {
  2. private:
  3. int _capacity;
  4. list<pair<int, int>> _cache;
  5. unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
  6. public:
  7. LRUCache(int capacity) {
  8. _capacity = capacity;
  9. }
  10. int get(int key) {
  11. if (_hashmap.find(key) == _hashmap.end()) {
  12. return -1;
  13. }
  14. auto iter = _hashmap[key];
  15. int value = iter->second;
  16. _cache.erase(iter);
  17. _cache.push_front({key, value});
  18. _hashmap[key] = _cache.begin();
  19. return value;
  20. }
  21. void put(int key, int value) {
  22. if (_hashmap.find(key) != _hashmap.end()) {
  23. auto iter = _hashmap[key];
  24. _cache.erase(iter);
  25. } else if (_cache.size() >= _capacity) {
  26. auto hashKey = _cache.back().first;
  27. _hashmap.erase(hashKey);
  28. _cache.pop_back();
  29. }
  30. _cache.push_front({key, value});
  31. _hashmap[key] = _cache.begin();
  32. }
  33. };
  34. /**
  35. * Your LRUCache object will be instantiated and called as such:
  36. * LRUCache* obj = new LRUCache(capacity);
  37. * int param_1 = obj->get(key);
  38. * obj->put(key,value);
  39. */

460. LFU 缓存

我们定义两个哈希表,

第一个 freq_table 以频率 freq 为索引,每个索引存放一个双向链表,这个链表里存放所有使用频率为 freq 的缓存,缓存里存放三个信息,分别为键 key,值 value,以及使用频率 freq。

第二个 key_table 以键值 key 为索引,每个索引存放对应缓存在 freq_table 中链表里的内存地址,这样我们就能利用两个哈希表来使得两个操作的时间复杂度均为 O(1)。

同时需要记录一个当前缓存最少使用的频率 minFreq,这是为了删除操作服务的。

  1. struct Node
  2. {
  3. int key;
  4. int val;
  5. int freq;
  6. Node(int _key, int _val, int _freq) : key(_key), val(_val), freq(_freq) {}
  7. };
  8. class LFUCache {
  9. private:
  10. int capacity;
  11. int minfreq;
  12. unordered_map<int, list<Node>> freq_table;
  13. unordered_map<int, list<Node>::iterator> key_table;
  14. public:
  15. LFUCache(int _capacity) {
  16. minfreq = 0;
  17. capacity = _capacity;
  18. key_table.clear();
  19. freq_table.clear();
  20. }
  21. int get(int key) {
  22. // 啥也没有
  23. if (capacity == 0) {
  24. return -1;
  25. }
  26. // 没有这个key
  27. if (!key_table.count(key)) {
  28. return -1;
  29. }
  30. auto iter = key_table[key];
  31. int val = iter->val;
  32. int freq = iter->freq;
  33. //查到删除旧的,增加新的
  34. freq_table[freq].erase(iter);
  35. //中间要判断一下此freq的链表是不是空了
  36. if (freq_table[freq].size() == 0) {
  37. //删除
  38. freq_table.erase(freq);
  39. //更新 minfreq
  40. if(minfreq == freq) minfreq += 1;
  41. }
  42. //插入新的
  43. freq_table[freq + 1].push_front(Node(key, val, freq + 1));
  44. key_table[key] = freq_table[freq + 1].begin();
  45. return val;
  46. }
  47. void put(int key, int value) {
  48. // 啥也没有
  49. if (capacity == 0) {
  50. return;
  51. }
  52. // 没有的话,加进去
  53. if (!key_table.count(key))
  54. {
  55. // 存储key的数量满了
  56. if (key_table.size() == capacity)
  57. {
  58. // 删掉频率最小的最早使用的
  59. auto node = freq_table[minfreq].back();
  60. int k = node.key;
  61. key_table.erase(k);
  62. freq_table[minfreq].pop_back();
  63. // 同样,该链空了,删除
  64. if (freq_table[minfreq].size() == 0) {
  65. freq_table.erase(minfreq);
  66. }
  67. }
  68. // 插入
  69. freq_table[1].push_front(Node(key, value, 1));
  70. key_table[key] = freq_table[1].begin();
  71. minfreq = 1; //注意这里要更新minfreq,容易出现错误
  72. } else {//存在
  73. // 更新
  74. // 先删,后频率增加,插入
  75. auto node = key_table[key];
  76. int freq = node->freq;
  77. freq_table[freq].erase(node);
  78. // 同样链空了,删除
  79. if(freq_table[freq].size() == 0)
  80. {
  81. freq_table.erase(freq);
  82. if (minfreq == freq) {
  83. minfreq += 1;
  84. }
  85. }
  86. // 插入
  87. freq_table[freq + 1].push_front(Node(key, value, freq + 1));
  88. key_table[key] = freq_table[freq + 1].begin();
  89. }
  90. }
  91. };
  92. /**
  93. * Your LFUCache object will be instantiated and called as such:
  94. * LFUCache* obj = new LFUCache(capacity);
  95. * int param_1 = obj->get(key);
  96. * obj->put(key,value);
  97. */

FIFO 缓存

先进先出,明显使用哈希+队列

  1. #include <iostream>
  2. #include <queue>
  3. #include <unordered_map>
  4. class FIFOCache {
  5. private:
  6. std::queue<int> fifoQueue; // 存放key
  7. std::unordered_map<int, int> cache; // 存放key-value
  8. int capacity;
  9. public:
  10. FIFOCache(int capacity) : capacity(capacity) {}
  11. int get(int key) {
  12. if (cache.find(key) != cache.end()) {
  13. return cache[key];
  14. }
  15. return -1;
  16. }
  17. void put(int key, int value) {
  18. if (cache.size() >= capacity) {
  19. int keyToRemove = fifoQueue.front();
  20. fifoQueue.pop();
  21. cache.erase(keyToRemove);
  22. }
  23. cache[key] = value;
  24. fifoQueue.push(key);
  25. }
  26. };
  27. int main() {
  28. FIFOCache cache(2);
  29. cache.put(1, 1);
  30. cache.put(2, 2);
  31. std::cout << cache.get(1) << std::endl; // 输出:1
  32. cache.put(3, 3);
  33. std::cout << cache.get(2) << std::endl; // 输出:-1,因为元素2被淘汰了
  34. return 0;
  35. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/533954
推荐阅读
相关标签
  

闽ICP备14008679号