LeetCode 146 LRU Cache的实现 Pyhton3

原题链接:链接

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

这道题蕴含了LRU缓存算法的实现原理,该算法被广泛应用于资源信息如图片的缓存,提供了一种灵活的缓存策略。在理解这道题之前,需要了解HashMap的原理。HashMap提供了key-value的存储方式,并提供了put和get方法来进行数据存取。但是HashMap是无序的,而现实中我们希望有顺序地去存储key-value时,就需要使用LinkedHashMap了,其中LinkedHashMap的实现也就是利用了双向链表+哈希表。Python的collection模块有一个双向队列deque可以辅助我们较轻松地完成此题,但为了学习,我们打算手写一遍一些算法的实现,以理清细节。

算法思路:

  1. 创建一个Node类,用于保存键、值以及前后指针的属性
class Node:
	def __init__(self, key, val):
		self.key = key
		self.val = val
		self.pre = None
		self.nxt = None
  1. 创建一个DList类,其中具备头节点和尾节点,并拥有添加节点和删除节点两个方法。
class DLinked:
	def __init__(self, head=None, tail=None):
		self.head = head
		self.tail = tail
	# 双向链表添加节点的方法
	def add(self, node):
		pass
	def remove(self, node):
		pass
  1. get(key):如果key存在于保存信息的哈希表hashTable中,则将其在链表中移除并重新加入到表头,并同时返回该值;其中表头的元素代表着最新的元素;反之,如果key不存在,则直接返回-1.
def get(key):
	# 先取出节点
	# 如果不存在 则返回None
	node = self.hashTable.get(key, None)
	if node:
		# 双向链表实例
		val = node.val
		self.dlinked.remove(node)
		# 并添加到表头
		self.dlinked.add(node)
		return val
	return -1
  1. put(key, value):如果key存在于保存信息的哈希表中,同样地将相应的节点移除并重新添加到表头,同时更新value值;如果不存在且达到了缓存的最大容量capacity,则将最近最少使用的元素,也就是表尾的元素删除,并也将其从hashTable中删除,将当前key, value分别加入到dlinked以及hashTable中;如果不存在且未达到最大容量,则直接加入即可。
def put(key, value):
	node = self.hashTable.get(key, None)
	# 如果存在
	if node:
		self.dlinked.remove(node)
		node.val = value
		self.dlinked.add(node)
		return
	# 如果不存在且达到了最大容量
	if self.capacity == len(self.hashTable):
		# 获取节点
		tailNode = self.dlinked.tail
		# 删除
		# 删除节点方法返回一个 key
		oldKey = self.dlinked.remove(tailNode)
		del self.hashTable[oldKey]
		# 加入节点
		# 加入方法返回新加这个节点
		node = self.dlinked.add(Node(key, value))
		self.hashTable[key] = node
	# 否则 直接加入
	else:
		node = self.dlinked.add(Node(key, value))
		self.hashTable[key] = node
	return		

剩下的工作就是完善双向链表的addremove方法。

首先是add方法,如果链表没有表头,则直接将节点设为头部,否则加入表头:

def add(self, node):
	# 如果双向链表没有头节点
	if self.head == None:
		self.head = node
		# 同时如果没有尾节点
		if self.tail == None:
			self.tail = node
	else:
		# 将节点加入表头
		self.head.pre = node
		node.nxt = self.head
		node.pre = None
		self.head = node
	return self.head

remove方法稍微复杂一些,需要根据删除的节点在原双向链表的位置进行判断,分别是表头、表中间和表尾处:

def remove(self, node):
	# 首先获取要删除节点的前后指针
	pre, nxt = node.pre, node.nxt
	# 如果只有一个节点
	if pre is None and nxt is None:
		self.head = None
		self.tail = None
		return node.key
	# 如果是头节点 且有后续节点
	if pre is None:
		nxt.pre = None
		self.head = nxt
	# 如果是尾节点 且有前续节点
	elif nxt is None:
		pre.nxt = None
		self.tail = pre
	# 中间节点
	else:
		pre.nxt = nxt
		nxt.pre = pre
	# 返回被删除节点的键
	return node.key

最后整合一块完事。没想到LRU Cache看起来挺简单,实现起来却不轻松啊。同志仍需努力啊。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Key Board

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值