python实现链表的时间复杂度_【leetcode系列】【算法】【困难】LFU缓存(hash + 双向链表,O(1)时间复杂度)...

题目:

解题思路:

LFU全称为Least Frequently Used,最近最少使用,算法的思路是:如果一个数据使用的次数很少,那么在将来被使用的概率应该也比较低,所以在有限的内存空间下,当内存快满、或者已经满了的情况下,优先删除此部分数据,以便于节省空间给其他数据

LFU与LRU想对应,LRU全称为Least Recently Used,最近最久未使用,算法的思路是:如果一个数据很久都未被使用,那么在之后的一段时间内,被使用的概率也比较低,所以优先删除此部分数据

LFU的思路是基于使用次数的,LRU的思路是基于使用时间的

回到本题,为了实现O(1)的时间复杂度,最常用的方式为:

查找 : 使用hash表

插入 :链表

单向链表需在删除已有元素时,需要遍历链表,才能将需要删除的节点前后节点连接起来,所以将链表修改为双向链表

查找 : hash表

插入 + 删除 : 双向链表

本题的整体思路就比较清晰了:

变量:

keyMap : hash表,key值为put时的key值,value值为在freqMap保存的双向链表中的节点地址(指针)

freqMap : hash表,key值为使用频率,value值为当前使用频率按照时间序的双向链表,链表每个节点中保存的值为:

put时的key值

put时的value值

当前值的使用频率,因为从keyMap找到时,无法知道freqMap中的key值,所以需要在node中保存此值

当前节点的上个节点prev

当前节点的下个节点next

capacity : 最大可以保存的值个数

size : 当前已有的值个数

minFreq : 当前使用频率最小的频率

流程:

get(self, key: int)操作:

在keyMap中查找key值,如果不存在,返回-1;如果存在,获取到对应的节点node

更新node对应的频率freq = freq + 1,在当前链表中删除此节点

将更新freq后的node重新插入到freqMap中,新freq对应的链表头,以便保证链表整体是有序的

返回node中保存的value值

put(self, key: int, value: int)操作:

在keyMap中查找key值,如果不存在,插入新的key值到keyMap和freqMap中;如果存在,则更新node中对应的freq和value值,并更新node的位置,更新方式同get操作中的2、3、4步骤

更新已保存的值个数size值

如果size <= capacity,则直接退出;如果size > capacity,则根据minFreq,删除对应链表中的最后一个节点

代码实现:

class Node:

def __init__(self, key: int, value: int, freq = 0, prev_node = None, next_node = None):

self.key = key

self.value = value

self.freq = freq

self.prev_node = prev_node

self.next_node = next_node

def insert(self, node) -> None:

# insert操作为常用操作,添加一个接口便于使用

node.prev_node = self

node.next_node = self.next_node

self.next_node.prev_node = node

self.next_node = node

def create_linked_lst() -> set((Node, Node)):

# 创建首尾dummy节点,避免一些临界情况的判断

# 并且方便管理

head = Node(0,0)

tail = Node(0,0)

head.next_node = tail

tail.prev_node = head

return (head, tail)

class LFUCache:

def __init__(self, capacity: int):

self.__freq_map = collections.defaultdict(create_linked_lst)

self.__key_map = {}

self.__capacity = capacity

self.__size = 0

self.__min_freq = 0

def __delete_node(self, node: Node) -> int:

# 在put中调用__update_node时,node尚未设置前后置节点

# 此时node.prev_node是None,需要保护性判断

if node.prev_node:

node.prev_node.next_node = node.next_node

node.next_node.prev_node = node.prev_node

# 如果删除完成后,当前频率下没有有效节点了,则在__freq_map频率表中删除此列表

if node.prev_node is self.__freq_map[node.freq][0] and node.next_node is self.__freq_map[node.freq][-1]:

self.__freq_map.pop(node.freq)

return node.key

def __update_node(self, node: Node):

node.freq += 1

self.__delete_node(node)

self.__freq_map[node.freq][-1].prev_node.insert(node)

if node.freq == 1:

# 如果当前频率是1,不可能有比1还小的频率,此时更新最小频率为1

self.__min_freq = 1

elif self.__min_freq == node.freq - 1:

# 如果最小频率是当前node更新前的频率,需要判断之前的频率链表中,是否还有有效节点

# 如果没有,则删除对应的链表

head, tail = self.__freq_map[node.freq - 1]

if head.next_node is tail:

self.__min_freq = node.freq

def get(self, key: int) -> int:

if key in self.__key_map:

# 更新节点位置

self.__update_node(self.__key_map[key])

return self.__key_map[key].value

return -1

def put(self, key: int, value: int) -> None:

if self.__capacity > 0:

if key in self.__key_map:

# 如果当前key已经在__key_map中,则更新对应的值

node = self.__key_map[key]

node.value = value

else:

# 如果不存在,则创建一个新节点

node = Node(key, value)

self.__key_map[key] = node

self.__size += 1

if self.__size > self.__capacity:

# 节点个数已满,删除使用频率最小的存在时间最长的节点

self.__size -= 1

del_key = self.__delete_node(self.__freq_map[self.__min_freq][0].next_node)

self.__key_map.pop(del_key)

self.__update_node(node)

原文链接:https://blog.csdn.net/songyuwen0808/article/details/105423603

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值