146. LRU缓存

一种数据结构(哈希表+双向链表)

实现方式一:

  • 使用语言自带的、封装好的数据结构。在python中这种数据结构为collections包中的OrderedDict。
  • 其中move_to_end(key)函数,是将该key移到链表的最后。
  • popitem() 是 collections.OrderedDict 的方法,用于移除并返回一个键值对,其中``last=False 参数指定移除最先插入的项(最旧的)。

Code

python 复制代码
class LRUCache(collections.OrderedDict):      ### 使用语言自带的、封装好的数据结构(哈希表+双向链表)

    def __init__(self, capacity: int):
        self.capacity = capacity
        

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

        if key not in self:
            return -1
        
        self.move_to_end(key)
        return self[key]

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

        if key in self:
            self.move_to_end(key)
        
        self[key] = value
        if len(self) > self.capacity:
            self.popitem(last=False)        ## popitem 用于移除并返回一个键值对, last=False 参数指定移除最先插入的项(最旧的)


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

实现方式二:

自己手动创建一个哈希表+双向链表的数据结构。

Code

python 复制代码
class DLinkedNode:     ### 哈希表+双向链表的数据结构定义(这里只是其中的一个点)
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None


class LRUCache:

    def __init__(self, capacity: int):
        self.cache = dict()             ### 哈希表,用于快速判断是否存在, 存储这个key对应的node
        self.head = DLinkedNode()       ### 双向链表,用于定位存储的位置并进行修改
        self.tail = DLinkedNode()       ### head和tail都是dummy_node
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0      

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        
        node = self.cache[key]      ### 
        self.move_to_head(node)
        return node.value
        

    def put(self, key: int, value: int) -> None:
        
        if key in self.cache:
            node = self.cache[key]
            self.move_to_head(node)
            node.value = value
        else:
            node = DLinkedNode(key=key,value=value)
            self.cache[key] = node
            self.add_to_head(node)          ## 添加到头部
            self.size += 1
            if self.size > self.capacity:
                remove = self.remove_tail()
                self.cache.pop(remove.key)
                self.size -= 1
    
    def move_to_head(self, node):
        self.remove_node(node)
        self.add_to_head(node)

    def add_to_head(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def remove_tail(self):
        node = self.tail.prev
        self.remove_node(node)
        return node

    def remove_node(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

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

图解

  1. 数据结构初始化

head和tail是两个dummy_node,添加的节点位于这两个中间,其中越靠近head,越是最新添加进来的;相反,越靠近tail,则在缓存中待留的时间越久。双向链表中的每一个节点都是一个node,node下存储的是一个字典,我们可以通过存key,然后value是该key在双向链表中的node,这样的话就可以根据key快速定位到其在双向链表中的位置。

  1. get(), 查询函数

首先判断要get的key是否添加过,如果添加过的话,则根据key的值定位到node的位置,然后输出node下的value值,随后需要将该node移动到head处。

  1. put(),添加函数
  • 首先判断要添加的key是否添加过,如果添加过,则根据key的值定位到node的位置,然后修改node下的value值,随后需要将该node移动到head处。
  • 如果没有添加过,那么需要新创建一个node,来存储key和value,随后需要在一个字典下存储这个key和node,以方便后续根据key直接在双向链表中找到node的位置。
  • 如果添加后发现超出capacity,那需要移除最接近tail的尾部node。
  1. add_to_head(),添加到头部函数

需要修改head、靠近head的节点、添加节点的prev和next指针,从而将节点添加到头部。

  1. remove_node(),删除节点函数。

首先需要找到要删除节点的上一个节点和下一个节点,然后修改指针。

  1. move_to_head(),移动到头部函数。

那实际上可以通过先把这个节点删除remove_node(),然后再将这个节点添加到头部add_to_head()。

  1. remove_tail(),移动尾部的节点函数。

直接找到tail的上一个节点,然后用remove_node()直接对该节点进行删除。

相关推荐
深耕AI6 小时前
【13/20】缓存与性能优化:Redis 在 Express 中的整合,实现用户数据缓存
redis·缓存·性能优化
仟濹6 小时前
【力扣LeetCode】 1413_逐步求和得到正数的最小值
算法·leetcode·职场和发展
spiderwiner7 小时前
Part03 数据结构
数据结构·c++·csp
YoungHong19927 小时前
面试经典150题[037]:矩阵置零(LeetCode 73)
leetcode·面试·矩阵
未知陨落9 小时前
LeetCode:46.二叉树展开为链表
算法·leetcode·链表
小欣加油9 小时前
leetcode 206 反转链表
数据结构·c++·算法·leetcode·链表·职场和发展
野犬寒鸦9 小时前
力扣hot100:环形链表II(哈希算法与快慢指针法思路讲解)
java·数据结构·算法·leetcode·链表·哈希算法
PKNLP12 小时前
Python进阶指南7:排序算法和树
数据结构·python·排序算法
刚入坑的新人编程13 小时前
算法训练.15
数据结构·c++·算法·哈希算法