题目来源

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

描述

LRUCache - 图1

思路:双向链表+哈希表

双向链表的节点存放key-value,根据最近访问的次序,越靠近头节点,说明距离上次访问的时间越短。哈希表用来存放key-节点,用于快速定位key对应的节点在双向链表中的位置,或者判断key是否在LRU缓存中。

对于初始化操作LRUCache(int capacity),可以初始化双向链表,初始化哈希表、初始化capacity。

对于get操作,可以先使用哈希表判断缓存中是否存在key,若果不存在,直接返回-1,如果存在,则将key对应的双向链表中的节点挪到链表的表头,并且返回节点中的value。

对于put操作,可以先使用哈希表判断缓存中是否存在key,如果存在,则获取其对应节点,更新其value值,并将该节点挪到表头位置;如果不存在,则新建节点保存key-value,并将其插到表头位置,此外要在哈希表中添加key-节点键值对,此时缓存大小增加,因此要判断大小是否超过了capacity,如果超过了,需要将表尾的节点删除,同时删除表为节点在哈希表中的信息。

代码

  1. import java.util.HashMap;
  2. public class Main {
  3. public static void main(String[] args) {
  4. LRUCache lruCache = new LRUCache(2);
  5. System.out.println(lruCache.put(1, 0));
  6. System.out.println(lruCache.put(2, 2));
  7. System.out.println(lruCache.get(1));
  8. System.out.println(lruCache.put(3, 3));
  9. System.out.println(lruCache.get(2));
  10. System.out.println(lruCache.put(4, 4));
  11. System.out.println(lruCache.get(1));
  12. System.out.println(lruCache.get(3));
  13. System.out.println(lruCache.get(4));
  14. }
  15. }
  16. class DLinkedNode{
  17. DLinkedNode prev;
  18. DLinkedNode next;
  19. int key;
  20. int value;
  21. }
  22. class LRUCache{
  23. int capacity;
  24. int size;
  25. DLinkedNode dLinkedList = null;
  26. DLinkedNode head = null;
  27. DLinkedNode tail = null;
  28. HashMap<Integer, DLinkedNode> hashMap = null;
  29. //构造函数
  30. public LRUCache(int capacity){
  31. this.capacity = capacity;
  32. size = 0;
  33. DLinkedNode headDummy = new DLinkedNode();
  34. DLinkedNode tailDummy = new DLinkedNode();
  35. headDummy.next = tailDummy;
  36. headDummy.prev = null;
  37. tailDummy.next = null;
  38. tailDummy.prev = headDummy;
  39. head = headDummy;
  40. tail = tailDummy;
  41. hashMap = new HashMap<>();
  42. }
  43. /**
  44. * 获取关键字的值,如果没有,返回-1
  45. * 先通过hashMap获取到key所在链表的位置,如果没有,返回-1结束
  46. * 如果存在,将双向链表中对应的节点挪到头节点的位置,返回对应的value
  47. * @param key 获取key关键字的值
  48. * @return 返回key对应的值,如果没有,返回-1
  49. */
  50. int get(int key){
  51. if(!hashMap.containsKey(key)) return -1;
  52. DLinkedNode linkedNodeOfKey = hashMap.get(key); // 获取key对应节点
  53. linkedNodeOfKey.prev.next = linkedNodeOfKey.next;
  54. linkedNodeOfKey.next.prev = linkedNodeOfKey.prev;
  55. head.next.prev = linkedNodeOfKey;
  56. linkedNodeOfKey.prev = head;
  57. linkedNodeOfKey.next = head.next;
  58. head.next = linkedNodeOfKey;
  59. return linkedNodeOfKey.value;
  60. }
  61. /**
  62. * 将key-value添加到缓存中
  63. * 如果key已经存在,更新其value值,并将对应节点挪到头节点的位置
  64. * 如果key不存在,则新建节点保存key-value,并将该节点插入链表头部,并且将key和节点对添加到hashMap中,更新size,如果size超过capacity,删除链表末尾元素
  65. * @param key 待插入的key
  66. * @param value 待插入的value
  67. */
  68. Object put(int key, int value){
  69. DLinkedNode linkedNodeOfKey = null;
  70. if(hashMap.containsKey(key)){
  71. linkedNodeOfKey = hashMap.get(key);
  72. linkedNodeOfKey.value = value;
  73. linkedNodeOfKey.prev.next = linkedNodeOfKey.next;
  74. linkedNodeOfKey.next.prev = linkedNodeOfKey.prev;
  75. }else{
  76. linkedNodeOfKey = new DLinkedNode();
  77. linkedNodeOfKey.key = key;
  78. linkedNodeOfKey.value = value;
  79. hashMap.put(key, linkedNodeOfKey);
  80. size++;
  81. }
  82. head.next.prev = linkedNodeOfKey;
  83. linkedNodeOfKey.prev = head;
  84. linkedNodeOfKey.next = head.next;
  85. head.next = linkedNodeOfKey;
  86. if(size > capacity){
  87. int keyForDel = tail.prev.key;
  88. hashMap.remove(keyForDel);
  89. tail.prev.prev.next = tail;
  90. tail.prev = tail.prev.prev;
  91. size--;
  92. }
  93. return null;
  94. }
  95. }