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

LRU,Least Recently Used,即最近最少使用,是一种常用的页面置换算法。

LRU是操作系统为最大化页面命中率而广泛采用的一种页面置换算法。该算法的思路是,发生页面中断时,选择未使用时间最长的页面置换出去。

功能:

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

分析

  1. 使用双向链表实现的队列,队列的最大容量为缓存的大小。在使用过程中,把最近使用的页面移动到队列头,最近没有使用的页面将被放在队列尾的位置。
  2. 使用一个哈希表,把页号作为键,把缓存在队列中的结点的地址作为值。
  1. class DLinkedNode:
  2. def __init__(self, key=0, value=0) -> None:
  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) -> None:
  9. self.cache = dict()
  10. # 使用伪头部和伪节点
  11. self.head = DLinkedNode()
  12. self.tail = DLinkedNode()
  13. self.head.next = self.tail
  14. self.tail.next = self.head
  15. self.capacity = capacity
  16. self.size = 0
  17. def get(self, key: int) -> int:
  18. if key not in self.cache:
  19. return -1
  20. # 如果key存在,先通过哈希表定位,再移动头部
  21. node = self.cache[key]
  22. self.moveToHead(node)
  23. return node.value
  24. def put(self, key: int, value: int) -> None:
  25. if key not in self.cache:
  26. # 如果key不存在,创建一个新的节点
  27. node = DLinkedNode(key, value)
  28. # 添加哈希表
  29. self.cache[key] = node
  30. # 添加至双向链表的头部
  31. self.addToHead(node)
  32. self.size += 1
  33. if self.size > self.capacity:
  34. # 如果超出容量,删除双向链表的尾部节点
  35. removed = self.removeTail()
  36. # 删除哈希表中对应的项
  37. self.cache.pop(removed.key)
  38. self.size -= 1
  39. else:
  40. # 如果key存在,先通过哈希表定位,再修改value,并移到头部
  41. node = self.cache[key]
  42. node.value = value
  43. self.moveToHead(node)
  44. def addToHead(self, node):
  45. # 头结点添加node
  46. node.prev = self.head
  47. node.next = self.head.next
  48. self.head.next.prev = node
  49. self.head.next = node
  50. def removeNode(self, node):
  51. # 双向链表删除数据
  52. node.prev.next = node.next
  53. node.next.prev = node.prev
  54. def moveToHead(self, node):
  55. self.removeNode(node)
  56. self.addToHead(node)
  57. def removeTail(self):
  58. node = self.tail.prev
  59. self.removeNode(node)
  60. return node
  61. # Your LRUCache object will be instantiated and called as such:
  62. # obj = LRUCache(capacity)
  63. # param_1 = obj.get(key)
  64. # obj.put(key,value)