一:LRU Cache原理
详细请见链接:https://www.jianshu.com/p/74a4efacb0a7
二:Python实现
class LinkedNode(object):
def __init__(self, key, val): # 节点需要保存key,val
self.key = key
self.val = val
self.next = None
self.pre = None
class LRUCache(object):
def __init__(self, capacity):
self.capacity = capacity
self.hashMap = collections.defaultdict(LinkedNode) # key:LinkedNode
self.head = LinkedNode(-1, -1)
self.tail = LinkedNode(-2, -2)
self.head.next = self.tail
self.tail.pre = self.head
def get(self, key): # 判断缓存中是否存在? 修改访问顺序
"""
通过 HashMap 找到双向链表节点,
因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值.
"""
res = -1
if key in self.hashMap.keys():
curNode = self.hashMap[key]
res = curNode.val
# 修改访问顺序
self._remove(curNode)
self._add(curNode)
return res
def put(self, key, value): # 判断缓存空间是否满了? key为访问的序号
"""
首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。
如果不存在,需要构造新的节点,并且尝试把节点塞到队头,
如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key
"""
# 已经存在这个key了, 删了这个结点
if key in self.hashMap.keys():
self._remove(self.hashMap[key])
# 插入新节点, 这里其实就已经更新顺序了
curNode = LinkedNode(key, value)
self._add(curNode)
self.hashMap[key] = curNode # 加入hashMap中
# 满了要删了尾结点
if len(self.hashMap) > self.capacity:
last = self.tail.pre
self.hashMap.pop(last.key) # 删除的是尾结点的key,不是插入节点的key啊
self._remove(last)
def _remove(self, node): # 删除
p = node.pre
n = node.next
p.next = n
n.pre = p
def _add(self, node): # 插入到头结点之后
p = self.head.next
p.pre = node
node.next = p
self.head.next = node
node.pre = self.head
三:LFU Cache原理
参考:https://leetcode.com/problems/lfu-cache/discuss/207673/Python-concise-solution-detailed-explanation%3A-Two-dict-%2B-Doubly-linked-list
四:python实现
class Node:
def __init__(self, key, val):
self.key = key
self.val = val
self.freq = 1
self.prev = self.next = None
class DLinkedList:
def __init__(self):
self._sentinel = Node(None, None) # dummy node
self._sentinel.next = self._sentinel.prev = self._sentinel
self._size = 0
def __len__(self):
return self._size
def append(self, node):
node.next = self._sentinel.next
node.prev = self._sentinel
node.next.prev = node
self._sentinel.next = node
self._size += 1
def pop(self, node=None):
if self._size == 0:
return
if not node:
node = self._sentinel.prev
node.prev.next = node.next
node.next.prev = node.prev
self._size -= 1
return node
class LFUCache(object):
def __init__(self, capacity):
self._size = 0
self._capacity = capacity
self._node = dict() # key: Node
self._freq = collections.defaultdict(DLinkedList)
self._minfreq = 0
def _update(self, node):
freq = node.freq
self._freq[freq].pop(node)
if self._minfreq == freq and not self._freq[freq]:
self._minfreq += 1
node.freq += 1
freq = node.freq
self._freq[freq].append(node)
def get(self, key):
if key not in self._node:
return -1
node = self._node[key]
self._update(node)
return node.val
def put(self, key, value):
if self._capacity == 0:
return
if key in self._node:
node = self._node[key]
self._update(node)
node.val = value
else:
if self._size == self._capacity:
node = self._freq[self._minfreq].pop()
del self._node[node.key]
self._size -= 1
node = Node(key, value)
self._node[key] = node
self._freq[1].append(node)
self._minfreq = 1
self._size += 1