题目

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。


    进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例

  1. 输入
  2. ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
  3. [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
  4. 输出
  5. [null, null, null, 1, null, -1, null, -1, 3, 4]
  6. 解释
  7. LRUCache lRUCache = new LRUCache(2);
  8. lRUCache.put(1, 1); // 缓存是 {1=1}
  9. lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
  10. lRUCache.get(1); // 返回 1
  11. lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
  12. lRUCache.get(2); // 返回 -1 (未找到)
  13. lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
  14. lRUCache.get(1); // 返回 -1 (未找到)
  15. lRUCache.get(3); // 返回 3
  16. lRUCache.get(4); // 返回 4

实现

思路1 哈希表+双向链表

首先根据题目的get函数要求,我们可以建立一个哈希表以146. LRU缓存机制 - 图1的时间找到key对应的节点。
然后根据put的要求,我们需要对LRU进行添加和删除的操作,所以可以使用队列或者双向链表这样的数据结构来维护LRU里的节点。

  1. class DLinkNode:
  2. def __init__(self, key=0, value=0):
  3. self.key = key
  4. self.value = value
  5. self.prev = None
  6. self.next = None
  7. class LRUCache:
  8. def __init__(self, capacity: int):
  9. self.capacity = capacity
  10. self.hashmap = dict()
  11. self.head = DLinkNode()
  12. self.tail = DLinkNode()
  13. self.head.next = self.tail
  14. self.tail.prev = self.head
  15. self.size = 0
  16. def get(self, key: int) -> int:
  17. if key not in self.hashmap:
  18. return -1
  19. # 如果key存在,则先通过dict定位,再将节点移到头部
  20. node = self.hashmap[key]
  21. self.removeNode(node)
  22. self.addHead(node)
  23. return node.value
  24. def put(self, key: int, value: int) -> None:
  25. if key not in self.hashmap:
  26. # 创建一个新的节点加入哈希表和链表中
  27. node = DLinkNode(key, value)
  28. # 注意这里哈希表里存的是node
  29. self.hashmap[key] = node
  30. self.addHead(node)
  31. self.size += 1
  32. # 如果链表超度超过容量
  33. if self.size > self.capacity:
  34. removed = self.tail.prev
  35. self.removeNode(removed)
  36. self.hashmap.pop(removed.key)
  37. self.size -= 1
  38. else:
  39. # 如果存在则修改value,并移到头部
  40. node = self.hashmap[key]
  41. node.value = value
  42. self.removeNode(node)
  43. self.addHead(node)
  44. def removeNode(self, node):
  45. node.prev.next = node.next
  46. node.next.prev = node.prev
  47. def addHead(self, node):
  48. node.next = self.head.next
  49. node.prev = self.head
  50. self.head.next.prev = node
  51. self.head.next = node
  52. # Your LRUCache object will be instantiated and called as such:
  53. # obj = LRUCache(capacity)
  54. # param_1 = obj.get(key)
  55. # obj.put(key,value)

时间复杂度:putget操作都是
空间复杂度:146. LRU缓存机制 - 图2