【LeetCode】460 and 1132(LFU缓存机制)

LRU 算法的淘汰策略是 Least Recently Used,也就是每次淘汰那些最久没被使⽤的数据;
⽽ LFU 算法的淘汰策略是 Least Frequently Used,也就是每次淘汰那些使⽤次数最少的数据。

460. LFU缓存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解法:哈希链表+哈希表
LFU和LRU的主要区别在于缓存淘汰机制不同,前者是淘汰使用频率最低的数据,后者是淘汰最早使用的数据。
原先LRU算法的核心内容哈希链表,只能解决时序问题,无法解决频率信息。因此需要重新进行设计,实现LFU算法需要满足以下几个条件:
1、调⽤ get(key) ⽅法时,要返回该 key 对应的 val。
2、只要⽤ get 或者 put ⽅法访问⼀次某个 key,该 key 的 freq 就要加⼀。
3、如果在容量满了的时候进⾏插⼊,则需要将 freq 最⼩的 key 删除,如果最⼩的 freq 对应多个 key,则删除其中最旧的那⼀个。
针对1和2,我们可以通过维护相应的哈希表来解决,对于3,其有3个要注意的点,分别是最小freq,每个freq对应key,当freq相同时考虑时序。该实现为LFU算法核心,具体如下:
3.1、⾸先,肯定是需要 freq 到 key 的映射,⽤来找到 freq 最⼩的 key。
3.2、将 freq 最⼩的 key 删除,那你就得快速得到当前所有 key 最⼩的 freq 是多少。想要时间复杂度O(1) 的话,肯定不能遍历⼀遍去找,那就⽤⼀个变量 minFreq 来记录当前最⼩的 freq 吧。
3.3、可能有多个 key 拥有相同的 freq,所以 freq 对 key 是⼀对多的关系,即⼀个 freq 对应⼀个 key 的列表。
3.4、希望 freq 对应的 key 的列表是存在时序的,便于快速查找并删除最旧的 key。
3.5、希望能够快速删除 key 列表中的任何⼀个 key,因为如果频次为 freq的某个 key 被访问,那么它的频次就会变成 freq+1,就应该从 freq 对应的key 列表中删除,加到 freq+1 对应的 key 的列表中。
其中对于3.2-3.5,可以用key为freq,value为哈希链表的字典来解决。

哈希链表,顾名思义,是链表和哈希集合的结合体。链表不能快速访问链表节点,但是插⼊元素具有时序;哈希集合中的元素⽆序,但是可以对元素进⾏快速的访问和删除。那么,它俩结合起来就兼具了哈希集合和链表的特性,既可以在 O(1) 时间内访问或删除其中的元素,⼜可以保持插⼊的时序。
综上,LFU的实现如下:

# 双向链表
class DoubleList:
    def __init__(self):
        self.head = Node(-1, -1) 
        self.tail = Node(-1, -1)
        self.tail.prev = self.head
        self.head.next = self.tail
        self.size = 0
    def addLast(self, x):
        x.prev = self.tail.prev
        self.tail.prev.next = x
        x.next = self.tail
        self.tail.prev = x
        self.size += 1
    def removeNode(self, x):
        x.prev.next = x.next
        x.next.prev = x.prev
        self.size -= 1
    def removeFirst(self):
        key = self.head.next.key
        self.head.next.next.prev = self.head
        self.head.next = self.head.next.next
        self.size -= 1
        return key

class Node:
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LFUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.key2freq = {}
        self.freq2link = collections.defaultdict(DoubleList)
        self.key2node = {}
        self.key2value = {}
        self.freqcount = {}
        self.minFreq = float("-inf")
        self.size = 0

    def get(self, key: int) -> int:
        # print(key, self.key2freq)
        if key not in self.key2freq:
            return -1
        value = self.key2node[key].value
        # 更新状态
        freq = self.key2freq[key]
        prev_link = self.freq2link[freq]
        new_link = self.freq2link[freq+1]
        node = self.key2node[key]
        prev_link.removeNode(node)
        # print(freq, prev_link.size)
        if freq == self.minFreq and prev_link.size == 0:
            self.minFreq += 1
        new_link.addLast(node)
        self.key2freq[key] = freq + 1
        return value

    def put(self, key: int, value: int) -> None:
        if self.capacity <= 0:
            return
        if key in self.key2freq:
            # 存在当前key,更新其value
            # freq = self.key2freq[key]
            # 更新状态
            freq = self.key2freq[key]
            prev_link = self.freq2link[freq]
            new_link = self.freq2link[freq+1]
            node = self.key2node[key]
            node.value = value
            prev_link.removeNode(node)
            # 当前minFreq为空,增加minFreq
            if freq == self.minFreq and prev_link.size == 0:
                self.minFreq += 1
            new_link.addLast(node)
            self.key2freq[key] = freq + 1
            return
        if self.size == self.capacity:
            link = self.freq2link[self.minFreq]
            deletedKey = link.removeFirst()
            self.key2freq.pop(deletedKey)
            self.key2node.pop(deletedKey)
            self.size -= 1
        node = Node(key, value)
        self.key2freq[key] = 1
        self.key2node[key] = node
        self.freq2link[1].addLast(node)
        self.minFreq = 1
        self.size += 1



# Your LFUCache object will be instantiated and called as such:
# obj = LFUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

1332. 删除回文子序列

在这里插入图片描述

解法:直接判断
由于字符串本身只含有字母 ‘a’ 和 ‘b’ 两种字符,题目要求每次删除回文子序列(不一定连续)而使得字符串最终为空。题目中只包含两种不同的字符,由于相同的字符组成的子序列一定是回文子序列,因此最多只需要删除 2 次即可删除所有的字符。删除判断如下:

  • 如果该字符串本身为回文串,此时只需删除 1 次即可,删除次数为 1。
  • 如果该字符串本身不是回文串,此时只需删除 2 次即可,比如可以先删除所有的 ‘a’,再删除所有的 ‘b’,删除次数为 2。
class Solution:
    def removePalindromeSub(self, s: str) -> int:
        return 1 if s == s[::-1] else 2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值