问题描述:实现LRU缓存算法。
题目来源:146. LRU Cache
这个是一种缓存策略以及页面置换淘汰策略。简单的来说就是最近使用的不淘汰,最近没有使用的就要被淘汰。
题目要求put\set
都要实现常数时间的操作,则很容易想到利用哈希去做,但是如何来定义最远没有使用的呢?
1、加时间戳(timestamp),但是这样维护时间戳排序的时候很麻烦,时间复杂度很高
2、利用链表来维护当前的节点状态,每当有节点被唤醒的时候插入到头部,每当有新节点插入的时候去掉尾部的节点,双向链表可以实现这个功能,但是双向链表访问到某个节点时仍然有一个线性时间存在,所以再利用一个哈希表来存储每个节点。
明确一下几点:
- 1、当get的时候,如果key已经存在,需要把该key节点移位到头部,保持最新,如果不存在返回-1.
- 2、当put的时候,如果key不在里面,就直接插入到头结点,并检查时候超过最大存储容量,如果超过就剔除最后一个节点。
- 3、put的时候,如果key在里面,则需要找到key的位置,并将其重新插入到头部,当中有断链、连链的操作,是链表的基本操作。
代码实现如下:
class Node:
def __init__(self,x):
self.val = x
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
"""
:type capacity: int
"""
self.hash_map = {}
self.capacity = capacity
self.head = Node(None)
self.tail = Node(None)
self.head.prev = None
self.tail.next = None
self.head.next = self.tail
self.tail.prev = self.head
self.node_hm = {}
def get(self, key):
"""
:type key: int
:rtype: int
"""
if self.hash_map.get(key)!=None:
self.put(key,self.hash_map.get(key)) #更新操作如果在里面的话
return self.hash_map.get(key)
else:
return -1
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
tail--head
"""
if self.hash_map.get(key) != None:
#更新到头结点 无论是否超过容量
# p = self.head.next
p = self.node_hm.get(key)
tmp = self.head.next
if tmp != p:
p.next.prev = p.prev #连接左边的点
p.prev.next = p.next
p.prev = self.head #把p的前驱给head
self.head.next = p
p.next = tmp #后驱给下个节点
tmp.prev = p
self.hash_map[key] = value
self.node_hm[key] = p
else:
n = Node(key)
#如果没有超过容量,插入到头节点
tmp = self.head.next
self.head.next = n
n.prev = self.head
tmp.prev = n #左边两个链
n.next = tmp
#此时是最大容量,需要删减
self.hash_map[key] = value
self.node_hm[key] = n
# print(self.hash_map)
if len(self.hash_map) > self.capacity:
lastNode = self.tail.prev
lastNode.prev.next = self.tail #链接断链
self.tail.prev = lastNode.prev #后指
self.hash_map.pop(lastNode.val) #弹出字典
self.node_hm.pop(lastNode.val) #弹出字典
# del lastNode