题目描述

image.png

解题思路

本题主要思路是使用 双向链表 + hash表 来实现,把最近使用的缓存放在链表头,而链表尾就是最久未使用的缓存,所以删除的时候将链表尾删除,如果新增或者操作过的数据,应该放在链表头。

使用Java自带的LinkedHashMap(不推荐)

  1. // 使用提供的LinkedHashMap实现
  2. class LRUCache extends LinkedHashMap<Integer, Integer> {
  3. int capacity = 0;
  4. public LRUCache(int capacity) {
  5. super(capacity, 0.75F, true);
  6. this.capacity = capacity;
  7. }
  8. public int get(int key) {
  9. return super.getOrDefault(key, -1);
  10. }
  11. public void put(int key, int value) {
  12. super.put(key, value);
  13. }
  14. @Override
  15. protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
  16. return size() > capacity;
  17. }
  18. }

手动实现双向链表+hash表

步骤:
image.png
image.png

注意:使用虚拟头节点可以更加方便的找到最后一个节点和第一个节点,从而进行删除和插入。

  1. class LRUCache extends LinkedHashMap<Integer, Integer> {
  2. // 构造双向链表
  3. class DLinkedNode {
  4. int key;
  5. int value;
  6. DLinkedNode prev;
  7. DLinkedNode next;
  8. public DLinkedNode() {
  9. }
  10. public DLinkedNode(int key, int value) {
  11. this.key = key;
  12. this.value = value;
  13. }
  14. }
  15. // 初始化变量
  16. Map<Integer, DLinkedNode> map;
  17. DLinkedNode head, tail; // 首尾虚拟头节点
  18. int size = 0;
  19. int capacity = 0;
  20. public LRUCache(int capacity) {
  21. this.size = 0;
  22. this.capacity = capacity;
  23. map = new HashMap<>(capacity);
  24. // 初始化虚拟头尾节点
  25. head = new DLinkedNode();
  26. tail = new DLinkedNode();
  27. head.next = tail;
  28. tail.prev = head;
  29. }
  30. public int get(int key) {
  31. DLinkedNode dLinkedNode = map.get(key);
  32. if (dLinkedNode == null) {
  33. // 不存在直接返回-1
  34. return -1;
  35. }
  36. // 如果key存在,移动到头部
  37. moveToHead(dLinkedNode);
  38. return dLinkedNode.value;
  39. }
  40. public void put(int key, int value) {
  41. DLinkedNode dLinkedNode = map.get(key);
  42. if (dLinkedNode == null) {
  43. // 不存在,需要构建并放入头部
  44. DLinkedNode node = new DLinkedNode(key, value);
  45. addHead(node);
  46. // 哈希表中也放入映射关系
  47. map.put(key, node);
  48. size++;
  49. // 此时如果容量超过限定值,需要删除尾部元素
  50. if (size > capacity) {
  51. DLinkedNode tailPrevNode = removeTail();
  52. // 删除hash表
  53. map.remove(tailPrevNode.key);
  54. size--;
  55. }
  56. } else {
  57. // 如果存在,修改值并移动到头部
  58. dLinkedNode.value = value;
  59. moveToHead(dLinkedNode);
  60. }
  61. }
  62. // 加入头部
  63. public void addHead(DLinkedNode node) {
  64. node.next = head.next;
  65. node.prev = head;
  66. head.next.prev = node;
  67. head.next = node;
  68. }
  69. // 移动到头部
  70. public void moveToHead(DLinkedNode node) {
  71. removeNode(node);
  72. addHead(node);
  73. }
  74. // 删除指定节点
  75. public void removeNode(DLinkedNode node) {
  76. node.prev.next = node.next;
  77. node.next.prev = node.prev;
  78. }
  79. // 删除尾部节点
  80. public DLinkedNode removeTail() {
  81. DLinkedNode node = tail.prev;
  82. removeNode(node);
  83. return node;
  84. }
  85. }