运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
链接:https://leetcode-cn.com/problems/lru-cache

  1. LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
  2. cache.put(1, 1);
  3. cache.put(2, 2);
  4. cache.get(1); // 返回 1
  5. cache.put(3, 3); // 该操作会使得关键字 2 作废
  6. cache.get(2); // 返回 -1 (未找到)
  7. cache.put(4, 4); // 该操作会使得关键字 1 作废
  8. cache.get(1); // 返回 -1 (未找到)
  9. cache.get(3); // 返回 3
  10. cache.get(4); // 返回 4

哈希+双向链表

题解
哈希存key和链表节点。 用哈希的值存链表节点,为了处理链表节点时,不用去遍历链表。直接通过Key取值就好了。
链表存key和值。

image.png

  1. struct DLinkedNode {
  2. int key, value; //链表中存key,方便删除时,同时把哈希中的元素删除。 哈希值存链表节点,方便对链表节点操作。
  3. DLinkedNode* prev;
  4. DLinkedNode* next;
  5. DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
  6. DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
  7. };
  8. class LRUCache {
  9. private:
  10. unordered_map<int, DLinkedNode*> cache;
  11. DLinkedNode* head;
  12. DLinkedNode* tail;
  13. int size;
  14. int capacity;
  15. public:
  16. LRUCache(int _capacity): capacity(_capacity), size(0) {
  17. // 使用伪头部和伪尾部节点
  18. head = new DLinkedNode();
  19. tail = new DLinkedNode();
  20. head->next = tail;
  21. tail->prev = head;
  22. }
  23. int get(int key) {
  24. if (!cache.count(key)) {
  25. return -1;
  26. }
  27. // 如果 key 存在,先通过哈希表定位,再移到头部
  28. DLinkedNode* node = cache[key];
  29. moveToHead(node);
  30. return node->value;
  31. }
  32. void put(int key, int value) {
  33. if (!cache.count(key)) {
  34. // 如果 key 不存在,创建一个新的节点
  35. DLinkedNode* node = new DLinkedNode(key, value);
  36. // 添加进哈希表
  37. cache[key] = node;
  38. // 添加至双向链表的头部
  39. addToHead(node);
  40. ++size;
  41. if (size > capacity) {
  42. // 如果超出容量,删除双向链表的尾部节点
  43. DLinkedNode* removed = removeTail();
  44. // 删除哈希表中对应的项
  45. cache.erase(removed->key);
  46. // 防止内存泄漏
  47. delete removed;
  48. --size;
  49. }
  50. }
  51. else {
  52. // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
  53. DLinkedNode* node = cache[key];
  54. node->value = value;
  55. moveToHead(node);
  56. }
  57. }
  58. void addToHead(DLinkedNode* node) {
  59. node->prev = head;
  60. node->next = head->next;
  61. head->next->prev = node;
  62. head->next = node;
  63. }
  64. void removeNode(DLinkedNode* node) {
  65. node->prev->next = node->next;
  66. node->next->prev = node->prev;
  67. }
  68. void moveToHead(DLinkedNode* node) {
  69. removeNode(node);
  70. addToHead(node);
  71. }
  72. DLinkedNode* removeTail() {
  73. DLinkedNode* node = tail->prev;
  74. removeNode(node);
  75. return node;
  76. }
  77. };