https://leetcode.com/problems/lru-cache/
题目描述
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.
模拟LRU存储过程, 以下是来自维基百科的解释. 总结起来就是当cache满了以后, 把最久未操作的地址内容清空出去, 以放置最新的数据.
Least recently used (LRU)
Discards the least recently used items first. This algorithm requires keeping track of what was used when, which is expensive if one wants to make sure the algorithm always discards the least recently used item. General implementations of this technique require keeping “age bits” for cache-lines and track the “Least Recently Used” cache-line based on age-bits. In such an implementation, every time a cache-line is used, the age of all other cache-lines changes. LRU is actually a family of caching algorithms with members including 2Q by Theodore Johnson and Dennis Shasha, and LRU/K by Pat O’Neil, Betty O’Neil and Gerhard Weikum.
The access sequence for the below example is A B C D E D F.
LRU working
In the above example once A B C D gets installed in the blocks with sequence numbers (Increment 1 for each new Access) and when E is accessed, it is a miss and it needs to be installed in one of the blocks. According to the LRU Algorithm, since A has the lowest Rank(A(0)), E will replace A.
解题思路
先考虑不使用有序字典, 建立三个列表, cache存放数据, cacheKey存放索引值, evictQ存放地址时序序列, 队首是最久未操作过的地址, 队尾是最新操作过的地址.
本题的核心就是维护一个队列evictQ, 该队列保存的是各个地址的操作时序情况. 每次操作过某一地址的数据 (无论get还是put), 就将当前地址值放入evictQ的队尾. cache满的时候, 弹出队首地址的内容, 放入数据.
get和put操作的时间复杂度都是O(1), 空间复杂的是O(n)
代码
class LRUCache:
def __init__(self, capacity: int):
self.cache = [] # 存放数据
self.cacheKey = [] # 存放索引值
self.evictQ = [_ for _ in range(capacity)] # the order of evicting #操作时序队列, 队首是最久未操作过的地址, 队尾是最新操作过的地址
self.capacity = capacity
self.used = 0
def get(self, key: int) -> int:
if key in self.cacheKey:
index = self.cacheKey.index(key)
self.evictQ.remove(index)
self.evictQ.append(index)
return self.cache[index]
else:
return -1
def put(self, key: int, value: int) -> None:
# if the key is already in the cache
if key in self.cacheKey:
index = self.cacheKey.index(key)
self.evictQ.remove(index)
self.evictQ.append(index)
self.cache[index] = value
# if the key is not exist
# 1. if the cache is not full
# 2. if the cache is full, evict the first of the cache
else:
if self.used != self.capacity:
index = self.evictQ.pop(0)
self.cacheKey.insert(index, key)
self.cache.insert(index, value)
self.evictQ.append(index) # index move to the tail
self.used += 1
#
elif self.used == self.capacity:
index = self.evictQ.pop(0) # pop the head in the queue
self.cacheKey[index] = key
self.cache[index] = value
self.evictQ.append(index)