当前位置:   article > 正文

LeetCode - 146 LRU缓存(Java & JS & Py & C & C++)

LeetCode - 146 LRU缓存(Java & JS & Py & C & C++)

题目来源

146. LRU 缓存 - 力扣(LeetCode)

题目描述

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例

输入

["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

输出

[null, null, null, 1, null, -1, null, -1, 3, 4]

说明

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

提示

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 10^5 次 get 和 put

题目解析

LRU缓存表需要实现如下两个操作:

  • get(key):
  1. 如果对应key存在,则返回key对应的val,并更新对应key为最近访问。
  2. 如果对应key不存在,则返回-1。
  • put(key, val):
  1. 如果对应key存在,则更新key的val,并更新对应key为最近访问。
  2. 如果对应key不存在,则需要先检查缓存表是否满了:如果满了,则需要删除最久未使用的key,否则不做删除动作。
  3. 之后新增key-val,并更新key为最近访问。

LRU和LFU的区别:

当空间不足时,LRU算法优先删除最久未使用的key。而LFU算法是优先删除最少使用的key,如果有多个最少使用的key,则找出其中最久未使用的key删除。

LRU的实现原理:

LRU缓存结构可以基于哈希表map和双向链表link来实现。

  • 哈希表map的键即为本题的key,值则是Node(key, val),即包装了本题key,val的节点对象。
  • 双向链表link的节点元素即为Node。

这里哈希表map的值列 和 双向链表的节点 是共享Node的。如下图所示:

初始时,LRU缓存结构如下

lru.put(1,1)

通过哈希表map的键列,我们可以快速判断key是否已存在于LRU缓存中。

由于map不存在key=1,因此当前put是新增操作,且由于map中记录键数量<capacity,即容量足够,因此无需执行逐出操作。

下面我需要创建节点 Node(key=1, val=1),并构建如下关系:

  • map的键列插入key=1,并key=1的值列和 Node(key=1, val=1) 关联
  • 将 Node(key=1, val=1) 头插入到双向链表Link 中(离链表头head越近,即为最近使用,离链表尾tail越近,即为最久未使用)

lru.put(2,2)

由于map不存在key=2的键,因此是put是新增操作,检查容量足够。

因此创建 Node(key=2, val=2) 后,构建如下关系。

lru.get(1)

由于map存在key=1的键,因此可以根据map[key=1]找到对应的节点Node(key=1, val=1),并获取Node.val返回。

另外,get操作会更新key=1为最近使用,因此我们需要将Node(key=1, val=1)暂时从Link中移除,然后头插到Link。

lru.put(3,3)

由于map不存在key=3的键,因此是put是新增操作,但是此时我们发现map中记录了两个键,分别为key=1,key=2,已经装满了capacity=2的LRU缓存表,因此put新增操作前需要先逐出最久未使用的key。


前面我们说明过,离Link链表头越近的节点,则为最近使用,离Link链表尾越近的节点,则为最久未使用。

因此 Link.tail.prev 节点即为最久未使用的节点,此时我们需要删除它:

  • Link中删除该节点
  • 根据节点的Node.key,找到map[Node.key]的键,并删除该键

删除最久未使用后,容量足够放下新节点Node(key=3, val=3)

以上即为LRU缓存的实现原理的图示。大家可以对照下面代码实现,继续想想后面的图示。

Java算法源码

  1. import java.util.HashMap;
  2. class LRUCache {
  3. static class Node {
  4. int key;
  5. int val;
  6. Node prev;
  7. Node next;
  8. public Node() {};
  9. public Node(int key, int val) {
  10. this.key = key;
  11. this.val = val;
  12. }
  13. }
  14. static class Link {
  15. Node head;
  16. Node tail;
  17. public Link() {
  18. this.head = new Node();
  19. this.tail = new Node();
  20. this.head.next = this.tail;
  21. this.tail.prev = this.head;
  22. }
  23. public void removeNode(Node node) {
  24. node.prev.next = node.next;
  25. node.next.prev = node.prev;
  26. node.prev = null;
  27. node.next = null;
  28. }
  29. public void addFirst(Node node) {
  30. node.next = this.head.next;
  31. this.head.next.prev = node;
  32. this.head.next = node;
  33. node.prev = this.head;
  34. }
  35. }
  36. int capacity;
  37. Link link;
  38. HashMap<Integer, Node> map;
  39. public LRUCache(int capacity) {
  40. this.capacity = capacity;
  41. this.link = new Link();
  42. this.map = new HashMap<>();
  43. }
  44. public int get(int key) {
  45. if (this.map.containsKey(key)) {
  46. Node node = this.map.get(key);
  47. this.link.removeNode(node);
  48. this.link.addFirst(node);
  49. return node.val;
  50. } else {
  51. return -1;
  52. }
  53. }
  54. public void put(int key, int value) {
  55. if (this.map.containsKey(key)) {
  56. Node node = this.map.get(key);
  57. this.link.removeNode(node);
  58. this.link.addFirst(node);
  59. node.val = value;
  60. } else {
  61. if (this.map.size() == this.capacity) {
  62. Node removed = this.link.tail.prev;
  63. this.link.removeNode(removed);
  64. this.map.remove(removed.key);
  65. }
  66. Node newNode = new Node(key, value);
  67. this.link.addFirst(newNode);
  68. this.map.put(key, newNode);
  69. }
  70. }
  71. }
  72. //public class Main {
  73. // public static void main(String[] args) {
  74. // LRUCache lRUCache = new LRUCache(2);
  75. // lRUCache.put(1, 1); // 缓存是 {1=1}
  76. // lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
  77. // System.out.println(lRUCache.get(1)); // 返回 1
  78. // lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
  79. // System.out.println(lRUCache.get(2)); // 返回 -1 (未找到)
  80. // lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
  81. // System.out.println(lRUCache.get(1)); // 返回 -1 (未找到)
  82. // System.out.println(lRUCache.get(3)); // 返回 3
  83. // System.out.println(lRUCache.get(4)); // 返回 4
  84. // }
  85. //}

 

JS算法源码

  1. class Node {
  2. constructor(key, val) {
  3. this.key = key;
  4. this.val = val;
  5. this.next = null;
  6. this.prev = null;
  7. }
  8. }
  9. class Link {
  10. constructor() {
  11. this.head = new Node();
  12. this.tail = new Node();
  13. this.head.next = this.tail;
  14. this.tail.prev = this.head;
  15. }
  16. removeNode(node) {
  17. node.prev.next = node.next;
  18. node.next.prev = node.prev;
  19. node.prev = null;
  20. node.next = null;
  21. }
  22. addFirst(node) {
  23. node.next = this.head.next;
  24. this.head.next.prev = node;
  25. this.head.next = node;
  26. node.prev = this.head;
  27. }
  28. }
  29. /**
  30. * @param {number} capacity
  31. */
  32. var LRUCache = function (capacity) {
  33. this.capacity = capacity;
  34. this.map = new Map();
  35. this.link = new Link();
  36. };
  37. /**
  38. * @param {number} key
  39. * @return {number}
  40. */
  41. LRUCache.prototype.get = function (key) {
  42. if (this.map.has(key)) {
  43. const node = this.map.get(key);
  44. this.link.removeNode(node);
  45. this.link.addFirst(node);
  46. return node.val;
  47. } else {
  48. return -1;
  49. }
  50. };
  51. /**
  52. * @param {number} key
  53. * @param {number} value
  54. * @return {void}
  55. */
  56. LRUCache.prototype.put = function (key, value) {
  57. if (this.map.has(key)) {
  58. const node = this.map.get(key);
  59. this.link.removeNode(node);
  60. this.link.addFirst(node);
  61. node.val = value;
  62. } else {
  63. if (this.map.size == this.capacity) {
  64. const removed = this.link.tail.prev;
  65. this.link.removeNode(removed);
  66. this.map.delete(removed.key);
  67. }
  68. const newNode = new Node(key, value);
  69. this.link.addFirst(newNode);
  70. this.map.set(key, newNode);
  71. }
  72. };
  73. // const lRUCache = new LRUCache(2);
  74. // lRUCache.put(1, 1); // 缓存是 {1=1}
  75. // lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
  76. // console.log(lRUCache.get(1)); // 返回 1
  77. // lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
  78. // console.log(lRUCache.get(2)); // 返回 -1 (未找到)
  79. // lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
  80. // console.log(lRUCache.get(1)); // 返回 -1 (未找到)
  81. // console.log(lRUCache.get(3)); // 返回 3
  82. // console.log(lRUCache.get(4)); // 返回 4

 

Py算法源码

  1. class Node:
  2. def __init__(self, key, val):
  3. self.key = key
  4. self.val = val
  5. self.prev = None
  6. self.next = None
  7. class Link:
  8. def __init__(self):
  9. self.head = Node(-1, -1)
  10. self.tail = Node(-1, -1)
  11. self.head.next = self.tail
  12. self.tail.prev = self.head
  13. def removeNode(self, node):
  14. node.prev.next = node.next
  15. node.next.prev = node.prev
  16. node.prev = None
  17. node.next = None
  18. def addFirst(self, node):
  19. node.next = self.head.next
  20. self.head.next.prev = node
  21. self.head.next = node
  22. node.prev = self.head
  23. class LRUCache(object):
  24. def __init__(self, capacity):
  25. """
  26. :type capacity: int
  27. """
  28. self.capacity = capacity
  29. self.map = {}
  30. self.link = Link()
  31. def get(self, key):
  32. """
  33. :type key: int
  34. :rtype: int
  35. """
  36. if key in self.map:
  37. node = self.map[key]
  38. self.link.removeNode(node)
  39. self.link.addFirst(node)
  40. return node.val
  41. else:
  42. return -1
  43. def put(self, key, value):
  44. """
  45. :type key: int
  46. :type value: int
  47. :rtype: None
  48. """
  49. if key in self.map:
  50. node = self.map[key]
  51. self.link.removeNode(node)
  52. self.link.addFirst(node)
  53. node.val = value
  54. else:
  55. if len(self.map) == self.capacity:
  56. node = self.link.tail.prev
  57. self.link.removeNode(node)
  58. self.map.pop(node.key)
  59. node = Node(key, value)
  60. self.link.addFirst(node)
  61. self.map[key] = node
  62. # if __name__ == '__main__':
  63. # lRUCache = LRUCache(2)
  64. # lRUCache.put(1, 1) # 缓存是 {1=1}
  65. # lRUCache.put(2, 2) # 缓存是 {1=1, 2=2}
  66. # print(lRUCache.get(1)) # 返回1
  67. # lRUCache.put(3, 3) # 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
  68. # print(lRUCache.get(2)) # 返回-1
  69. # lRUCache.put(4, 4) # 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
  70. # print(lRUCache.get(1)) # 返回-1
  71. # print(lRUCache.get(3)) # 返回3
  72. # print(lRUCache.get(4)) # 返回4

C算法源码

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_SIZE 10000
  4. typedef struct NODE {
  5. int key;
  6. int val;
  7. struct NODE *prev;
  8. struct NODE *next;
  9. } Node;
  10. Node *new_Node(int key, int val) {
  11. Node *node = (Node *) malloc(sizeof(Node));
  12. node->key = key;
  13. node->val = val;
  14. node->prev = NULL;
  15. node->next = NULL;
  16. return node;
  17. }
  18. typedef struct LINK {
  19. Node *head;
  20. Node *tail;
  21. } Link;
  22. Link *new_Link() {
  23. Link *link = (Link *) malloc(sizeof(Link));
  24. link->head = new_Node(-1, -1);
  25. link->tail = new_Node(-1, -1);
  26. link->head->next = link->tail;
  27. link->tail->prev = link->head;
  28. return link;
  29. }
  30. void removeNode(Node *node) {
  31. node->prev->next = node->next;
  32. node->next->prev = node->prev;
  33. node->prev = NULL;
  34. node->next = NULL;
  35. }
  36. void addFirst(Link *link, Node *node) {
  37. node->next = link->head->next;
  38. link->head->next->prev = node;
  39. link->head->next = node;
  40. node->prev = link->head;
  41. }
  42. typedef struct {
  43. int capacity;
  44. Node *map[MAX_SIZE];
  45. int map_size;
  46. Link *link;
  47. } LRUCache;
  48. LRUCache *lRUCacheCreate(int capacity) {
  49. LRUCache *lru = (LRUCache *) malloc(sizeof(LRUCache));
  50. lru->capacity = capacity;
  51. lru->link = new_Link();
  52. lru->map_size = 0;
  53. for (int i = 0; i < MAX_SIZE; i++) {
  54. lru->map[i] = NULL;
  55. }
  56. return lru;
  57. }
  58. int lRUCacheGet(LRUCache *lru, int key) {
  59. if (lru->map[key] != NULL) {
  60. Node *node = lru->map[key];
  61. removeNode(node);
  62. addFirst(lru->link, node);
  63. return node->val;
  64. } else {
  65. return -1;
  66. }
  67. }
  68. void lRUCachePut(LRUCache *lru, int key, int value) {
  69. if (lru->map[key] != NULL) {
  70. Node *node = lru->map[key];
  71. removeNode(node);
  72. addFirst(lru->link, node);
  73. node->val = value;
  74. } else {
  75. if (lru->map_size == lru->capacity) {
  76. Node *removed = lru->link->tail->prev;
  77. removeNode(removed);
  78. lru->map[removed->key] = NULL;
  79. lru->map_size--;
  80. }
  81. Node *newNode = new_Node(key, value);
  82. addFirst(lru->link, newNode);
  83. lru->map[key] = newNode;
  84. lru->map_size++;
  85. }
  86. }
  87. void lRUCacheFree(LRUCache *lru) {
  88. Node *cur = lru->link->head;
  89. while (cur != NULL) {
  90. Node *next = cur->next;
  91. free(cur);
  92. cur = next;
  93. }
  94. free(lru);
  95. }
  96. //int main() {
  97. // LRUCache *lru = lRUCacheCreate(2);
  98. // lRUCachePut(lru, 1, 1);
  99. // lRUCachePut(lru, 2, 2);
  100. // printf("%d\n", lRUCacheGet(lru, 1));
  101. // lRUCachePut(lru, 3, 3);
  102. // printf("%d\n", lRUCacheGet(lru, 2));
  103. // lRUCachePut(lru, 4, 4);
  104. // printf("%d\n", lRUCacheGet(lru, 1));
  105. // printf("%d\n", lRUCacheGet(lru, 3));
  106. // printf("%d\n", lRUCacheGet(lru, 4));
  107. //
  108. // lRUCacheFree(lru);
  109. //
  110. // return 0;
  111. //}

 

C++算法源码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. class Node {
  4. public:
  5. int key{-1};
  6. int val{-1};
  7. Node *prev{};
  8. Node *next{};
  9. Node() = default;
  10. Node(int key, int val) : key(key), val(val) {}
  11. };
  12. class Link {
  13. public:
  14. Node *head;
  15. Node *tail;
  16. Link() {
  17. this->head = new Node();
  18. this->tail = new Node();
  19. this->head->next = this->tail;
  20. this->tail->prev = this->head;
  21. }
  22. static void removeNode(Node *node) {
  23. node->next->prev = node->prev;
  24. node->prev->next = node->next;
  25. node->prev = nullptr;
  26. node->next = nullptr;
  27. }
  28. void addFirst(Node *node) const {
  29. node->next = this->head->next;
  30. this->head->next->prev = node;
  31. this->head->next = node;
  32. node->prev = this->head;
  33. }
  34. };
  35. class LRUCache {
  36. public:
  37. int capacity;
  38. map<int, Node *> map;
  39. Link *link;
  40. explicit LRUCache(int capacity) {
  41. this->capacity = capacity;
  42. this->link = new Link();
  43. }
  44. int get(int key) {
  45. if (this->map.count(key) > 0) {
  46. Node *node = this->map[key];
  47. Link::removeNode(node);
  48. this->link->addFirst(node);
  49. return node->val;
  50. } else {
  51. return -1;
  52. }
  53. }
  54. void put(int key, int value) {
  55. if (this->map.count(key) > 0) {
  56. Node *node = this->map[key];
  57. Link::removeNode(node);
  58. this->link->addFirst(node);
  59. node->val = value;
  60. } else {
  61. if (this->map.size() == this->capacity) {
  62. Node *removed = this->link->tail->prev;
  63. Link::removeNode(removed);
  64. this->map.erase(this->map.find(removed->key));
  65. }
  66. Node *newNode = new Node(key, value);
  67. this->link->addFirst(newNode);
  68. this->map[key] = newNode;
  69. }
  70. }
  71. };
  72. //int main() {
  73. // LRUCache lru(2);
  74. // lru.put(1, 1);
  75. // lru.put(2, 2);
  76. // cout << lru.get(1) << endl;
  77. // lru.put(3, 3);
  78. // cout << lru.get(2) << endl;
  79. // lru.put(4, 4);
  80. // cout << lru.get(1) << endl;
  81. // cout << lru.get(3) << endl;
  82. // cout << lru.get(4) << endl;
  83. //
  84. // return 0;
  85. //}

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

闽ICP备14008679号