LeetCode 460. LFU Cache 顺带总结一下LRU

231 篇文章 0 订阅

Design and implement a data structure for Least Frequently Used (LFU) 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 reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Note that the number of times an item is used is the number of calls to the get and put functions for that item since it was inserted. This number is set to zero when the item is removed.

 

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

 

Example:

LFUCache cache = new LFUCache( 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.get(3);       // returns 3.
cache.put(4, 4);    // evicts key 1.
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

------------------------------------------------------------------

这题需要知道频率,自然容易想到用一个dict来记录频率的key,dict中key对应的一个双向链表,用以前LRU的方式来维护这个双向链表,道理比较简单,题目的难点是操作比较多,容易乱和出错。个人总结了以下套路,从顶层入手,把链表节点搬家这件事情抽象成一个函数,把顶层的操作隐射成底层的函数,然后再去完成底层的代码。这种套路对于LRU也适用。

class Cache:
    def __init__(self, capacity: int):
        self._size,self._cap = 0,capacity
        #需要多少零件

    def renew(self,node):
        self.try_delete_in_llst(node) #先试图删
        #然后挪到node到该去的地方

    def get(self, key: int) -> int:
        if (key in self._dic):
            node = self._dic[key]
            self.renew(node) #用来链表节点挪动
            return node._v
        return -1

    def put(self, key: int, value: int) -> None:
        if (self._cap > 0):
            node = get_Node() #如果有就从现有的拿,如果没有就新new一个
            if (self._size > self._cap):
                self._size -= 1
                deleted_key = self.try_delete_in_llst(该删除的节点)
                self._dic.pop(deleted_key)
            self.renew(node)
            self._dic[key] = node

对于LFU,还需要注意几点:

  1. Dict的key也放在链表节点里,否则从node里没法对dict的key进行操作
  2. 何时更新最小频率?这部分适合放在renew的函数里,同时注意有两个条件:最小频率节点搬走 或者 新来一个频率为1的节点,最小频率节点搬走严格还需要清理一下dict,但是可以偷懒不做
  3. 用default_dict的参数是lambda表达式,可以帮助省一些代码

以下是LFU的代码:

from collections import defaultdict
class Node:
    def __init__(self,k,v):
        self._k,self._v,self._freq = k,v,0
        self._pre,self._nxt = None, None

class LFUCache:
    def get_empty_llst(self):
        head,tail = Node(0,0),Node(0,0)
        head._nxt, tail._pre = tail, head
        return (head,tail)

    def __init__(self, capacity: int):
        self._size,self._cap,self._min_freq = 0,capacity,0
        self._key_dic, self._freq_dic = {}, defaultdict(self.get_empty_llst)

    def get(self, key: int) -> int:
        if key in self._key_dic:
            node = self._key_dic[key]
            self.move_update_freq(node)
            return node._v
        return -1

    def try_delete_in_llst(self, node):
        if (node._pre):
            pre,nxt = node._pre, node._nxt
            node._nxt._pre,node._pre._nxt = pre,nxt
            return node._k

    def move_update_freq(self, node):
        node._freq += 1
        self.try_delete_in_llst(node)
        head,tail = self._freq_dic[node._freq]
        headnxt = head._nxt
        head._nxt,headnxt._pre,node._pre,node._nxt = node,node,head,headnxt #insert after head
        #bug3: miss node._freq == 1
        if (node._freq == 1 or (self._min_freq == node._freq - 1 and self._freq_dic[self._min_freq][0]._nxt == self._freq_dic[self._min_freq][1])):
            self._min_freq = node._freq

    def put(self, key: int, value: int) -> None:
        if self._cap > 0:
            if key in self._key_dic:
                node = self._key_dic[key]
                node._v = value
            else:
                node = Node(key,value)
                self._size += 1
                if (self._size > self._cap):
                    self._size -= 1 #bug2: forget this line
                    deleted_key = self.try_delete_in_llst(self._freq_dic[self._min_freq][1]._pre)
                    self._key_dic.pop(deleted_key)
            self._key_dic[key] = node #bug1: forget this line
            self.move_update_freq(node)

# cache = LFUCache(2)
# cache.put(1, 1)
# cache.put(2, 2)
# print(cache.get(1))       # returns 1
# cache.put(3, 3)    # evicts key 2
# print(cache.get(2))       # returns -1 (not found)
# print(cache.get(3))       # returns 3.
# cache.put(4, 4)    # evicts key 1.
# print(cache.get(1))       # returns -1 (not found)
# print(cache.get(3))       # returns 3
# print(cache.get(4))       # returns 4

cache = LFUCache(3)
cache.put(2, 2)
cache.put(1, 1)
print(cache.get(2)) #2
print(cache.get(1)) #1
print(cache.get(2)) #2
cache.put(3, 3)
cache.put(4, 4)
print(cache.get(3)) #-1
print(cache.get(2)) #2
print(cache.get(1)) #1
print(cache.get(4)) #4

再补一个用这种思路写的LRU代码:

class Node:
    def __init__(self,k,v):
        self._k,self._v = k,v
        self._pre,self._nxt = None,None

class LRUCache:
    def __init__(self, capacity: int):
        self._size,self._cap = 0,capacity
        self._dic = {}
        self._llst = self.get_empty_llst()

    def get_empty_llst(self):
        head,tail = Node(0,0),Node(0,0)
        head._nxt, tail._pre = tail, head
        return (head,tail)

    def get(self, key: int) -> int:
        if (key in self._dic):
            node = self._dic[key]
            self.renew(node)
            return node._v
        return -1

    def try_delete_in_llst(self, node):
        if (node._pre):
            pre,nxt = node._pre, node._nxt
            node._nxt._pre,node._pre._nxt = pre,nxt
            return node._k

    def renew(self, node):
        self.try_delete_in_llst(node)
        head,tail = self._llst
        headnxt = head._nxt
        head._nxt,headnxt._pre,node._pre,node._nxt = node,node,head,headnxt #insert after head

    def put(self, key: int, value: int) -> None:
        if (self._cap > 0):
            if (key in self._dic):
                node = self._dic[key]
                node._v = value
            else:
                node = Node(key,value)
                self._size += 1
                if (self._size > self._cap):
                    self._size -= 1
                    deleted_key = self.try_delete_in_llst(self._llst[1]._pre)
                    self._dic.pop(deleted_key)
            self.renew(node)
            self._dic[key] = node

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值