做十题不如做一题做十遍
本题思路来源:
书影博客
题意
为“最不常使用缓存”(LFU cache)设计实现数据结构。应当支持get和set操作。
get(key) - 如果存在key,返回其对应的value,否则返回-1。
set(key, value) - 如果不存在key,新增value;否则替换原始value。当缓存容量满时,应当将最不常使用的项目移除。如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目。
思路
题目要求O(1)操作,那么很容易想到要用hash来存储,但是由于LFU的算法还需要记录访问频度,所以大致的思考是要用两个hash,一切似乎都很顺利,但是会发现移除最近最少使用的项目这个操作似乎不能用O(1)操作来完成。这里就需要用链表来完成,让某个指针(head)指向当前Least Recently Used的内容。
让我们看看需要哪些东西。
首先是两个双向链表:
KeyNode中保存key(键),value(值),freq(频度),prev(前驱),next(后继)
FreqNode中保存freq(频度)、prev(前驱)、next(后继)、first(指向最新的KeyNode),last(指向最老的KeyNode)
LFUCache操作实现如下:
set(key, value):
如果capacity为0,忽略当前操作,结束
如果keyDict中包含key,则替换其value,更新节点频度,结束
否则,如果当前keyDict的长度 == capcity,移除head.last(频度最低且最老的KeyNode,removeKeyNode)
新增KeyNode(key, value),加入keyDict,并更新freqDict
get(key):
若keyDict中包含key,则更新节点频度,返回对应的value
否则,返回-1
节点频度的更新 increase 函数
从keyDict中找到对应的KeyNode,然后通过KeyNode的freq值,从freqDict找到对应的FreqNode
如果FreqNode的next节点不等于freq + 1,则在其右侧插入一个值为freq + 1的新FreqNode节点(insertFreqNodeAfter)
将KeyNode的freq值+1后,从当前KeyNode链表转移到新的FreqNode对应的KeyNode链表 (unlinkKey & linkKey)
如果KeyNode移动之后,原来的FreqNode对应的KeyNode链表为空,则删除原来的FreqNode (delFreqNode)
在操作完毕后如果涉及到head的变更,则更新head
class KeyNode(object):
def __init__(self, key, value, freq = 1):
self.key = key
self.value = value
self.freq = freq
self.prev = self.next = None
class FreqNode(object):
def __init__(self, freq, prev, next):
self.freq = freq
self.prev = prev
self.next = next
self.first = self.last = None
class LFUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.keyDict = dict()
self.freqDict = dict()
self.head = None
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key in self.keyDict:
keyNode = self.keyDict[key]
value = keyNode.value
self.increase(key, value)
return value
return -1
def set(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if self.capacity == 0:
return
if key in self.keyDict:
self.increase(key, value)
return
if len(self.keyDict) == self.capacity:
self.removeKeyNode(self.head.last)
self.insertKeyNode(key, value)
def increase(self, key, value):
"""
Increments the freq of an existing KeyNode<key, value> by 1.
:type key: str
:rtype: void
"""
keyNode = self.keyDict[key]
keyNode.value = value
freqNode = self.freqDict[keyNode.freq]
nextFreqNode = freqNode.next
keyNode.freq += 1
if nextFreqNode is None or nextFreqNode.freq > keyNode.freq:
nextFreqNode = self.insertFreqNodeAfter(keyNode.freq, freqNode)
self.unlinkKey(keyNode, freqNode)
self.linkKey(keyNode, nextFreqNode)
def insertKeyNode(self, key, value):
"""
Inserts a new KeyNode<key, value> with freq 1.
:type key: str
:rtype: void
"""
keyNode = self.keyDict[key] = KeyNode(key, value)
freqNode = self.freqDict.get(1)
if freqNode is None:
freqNode = self.freqDict[1] = FreqNode(1, None, self.head)
if self.head:
self.head.prev = freqNode
self.head = freqNode
self.linkKey(keyNode, freqNode)
def delFreqNode(self, freqNode):
"""
Delete freqNode.
:rtype: void
"""
prev, next = freqNode.prev, freqNode.next
if prev: prev.next = next
if next: next.prev = prev
if self.head == freqNode: self.head = next
del self.freqDict[freqNode.freq]
def insertFreqNodeAfter(self, freq, node):
"""
Insert a new FreqNode(freq) after node.
:rtype: FreqNode
"""
newNode = FreqNode(freq, node, node.next)
self.freqDict[freq] = newNode
if node.next: node.next.prev = newNode
node.next = newNode
return newNode
def removeKeyNode(self, keyNode):
"""
Remove keyNode
:rtype: void
"""
self.unlinkKey(keyNode, self.freqDict[keyNode.freq])
del self.keyDict[keyNode.key]
def unlinkKey(self, keyNode, freqNode):
"""
Unlink keyNode from freqNode
:rtype: void
"""
next, prev = keyNode.next, keyNode.prev
if prev: prev.next = next
if next: next.prev = prev
if freqNode.first == keyNode: freqNode.first = next
if freqNode.last == keyNode: freqNode.last = prev
if freqNode.first is None: self.delFreqNode(freqNode)
def linkKey(self, keyNode, freqNode):
"""
Link keyNode to freqNode
:rtype: void
"""
firstKeyNode = freqNode.first
keyNode.prev = None
keyNode.next = firstKeyNode
if firstKeyNode: firstKeyNode.prev = keyNode
freqNode.first = keyNode
if freqNode.last is None: freqNode.last = keyNode
# Your LFUCache object will be instantiated and called as such:
# obj = LFUCache(capacity)
# param_1 = obj.get(key)
# obj.set(key,value)