一: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