LeetCode(力扣) 146题:LRU缓存机制----哈希表+双向链表求解附带详细注释

题目描述

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间

示例

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

思路分析

题目描述中时让用自己掌握的数据结构设计实现一个LRU缓存机制,我第一反应是使用哈希表,通过对哈希表进行增删查改等操作完成LRU缓存机制的 ’ put,get '方法,刚开始想的是再定义一个哈希表用来存储各个元素被操作的次数,来判断该删除哪个元素,但是后来发现不行,这样还是不能表征缓存机制里元素里的使用情况(顺序问题),既然涉及到顺序问题,那么在数据结构中最能反映顺序的应该是链表了,在此处使用的是双向链表,但是我总觉得单向链表应该也可以吧,但是没有尝试,大家有兴趣的可以尝试一下,我在评论区等你们。下边说一下解题思路的大致逻辑。
初始化方法中定义一个带两个虚拟节点的双向链表和一个哈希表,并且明确LRU缓存的容量,以及当前缓存大小。
接下来便是添加元素,如果当前缓存中包含该元素,则只需要找出该元素,并改变其值就好了;
如果当前缓存中不包含该元素,先判断当前缓存是否已经到了上限,如果没到上限,那么直接添加,并把该节点移动到双向链表的最前端(在这里将最近操作的元素放在双向链表的开头位置,这样许久未操作的元素会排在双向链表的尾部);如果到了上限,则需删掉双向链表的尾部元素,并在缓存中删掉,然后添加新元素,并把新元素移动到双向链表的头部位置。
最后是取元素的方法,如果待取元素不在缓存中,直接返回 -1;如果待取元素在缓存中,则直接返回该元素的值。

注意:在这道题目中比较巧妙的就是使用双向链表+哈希表,双向链表的每个节点存储元素,双向链表的顺序表示了当前元素被操作的顺序情况(最近被操作的元素放在头部位置,最近没被操作过的放在尾部),方便我们删除当前最少被使用的元素(双向链表尾部元素)。缓存cache中则保留了key和value的映射关系。

思路和逻辑先讲到这,我们看一下代码的具体实现:

代码

# 双向链表的类定义
class dual_linkList_node:
    def __init__(self, key=0, val=0):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None
# LRU缓存机制的类定义
class LRUCache:
	# LRU缓存机制的初始化方法
    def __init__(self, capacity: int):
        self.cache = dict() # 初始化存储映射关系的字典
        self.head = dual_linkList_node() # 双向链表的头节点
        self.tail = dual_linkList_node() # 双向链表的尾节点
        self.head.next = self.tail # 头节点指向尾节点
        self.tail.prev = self.head # 尾节点指向头节点, 至此双向链表构建完成
        self.capacity = capacity # 初始化LRUCache的容量大小
        self.size = 0
    # 在这里将元素储存在双向链表中, 映射关系存在字典里 #
    def get(self, key: int) -> int: # 获取LRUCache中的某个元素
        if key in self.cache: # 若待取元素在cache内, 则直接根据key取出该节点, 再将该节点移动到双向链表的头部, 并返回该元素的值
            node = self.cache[key]
            self.move_to_head(node)
            return node.val
        else: # 如果待取元素不在cache内, 返回 -1
            return -1

    # 删除双向链表的一个元素
    def remove_node(self, node):
        node.prev.next = node.next # node的前一个节点向后指向node后一个节点
        node.next.prev = node.prev # node的后一个节点向前指向node前一个节点

    # 在head节点之后添加node
    def add_to_head(self, node):
        # 在双向链表中, node和self.head都需要进行变换
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    # node节点移动到双向链表头节点之后
    def move_to_head(self, node):
        self.remove_node(node) # 先在双向链表中移除该node节点
        self.add_to_head(node) # 再将该node节点添加在head之后

    # 移除双向链表尾节点
    def remove_tail(self):
    	# 因为头节点和尾节点都为虚拟节点,因此删除尾节点即删除虚拟尾节点的前一个节点
        node = self.tail.prev
        self.remove_node(node)
        return node

    # 添加元素
    def put(self, key: int, value: int) -> None:
        if key not in self.cache:
            # 如果 key 不存在,创建一个新的节点
            node = dual_linkList_node(key, value)
            # 添加进哈希表
            self.cache[key] = node
            # 添加至双向链表的头部
            self.add_to_head(node)
            self.size += 1
            if self.size > self.capacity:
                # 如果超出容量,删除双向链表的尾部节点
                removed = self.remove_tail()
                # 删除哈希表中对应的项
                self.cache.pop(removed.key)
                self.size -= 1
        else:
        	# 如果带输入的元素存在于cache,则只需取出存储该元素的节点,改变其值并移动到双向链表头节点的位置即可
            node = self.cache[key]
            node.val = value
            self.move_to_head(node)

运行结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Just_do_myself/article/details/118499267