leetcode - [双向链表、哈希表] -(146)LRU缓存机制

1、问题描述

运用你所掌握的数据结构,设计一个LRU(最近最少使用)缓存机制。它应该支持以下操作,获取数据get和写入数据put。
获取数据get(key):如果密钥存在于缓存中,则获取密钥的值(总是正数),否则返回-1;
写入数据put(key,value):如果密钥不存在,则写入其数据值,当缓存容量达到上限时,应该在写入新的数据值之前删除最近最少使用的数据值,从而从而为新的数据值腾出空间。如果密钥存在,则修改其数据值,此时该密钥对应的数据值是最近最多使用的数据值。
你是否可以在 O ( 1 ) O(1) O(1)时间内完成这两种操作?

2、解题思路

  • 分析:首先从一个示例出发
示例:
LRUCache cache = new LRUCache(2 /*缓存容量*/);
cache.put(1,1);  
cache.put(2,2);
cache.get(1);  //返回1,并使密钥1变为最近使用的密钥
cache.put(3,3); //该操作会使密钥2作废
cache.get(2); // 返回-1(未找到)
cache.put(1,5); // 该操作会使密钥1变为最近使用的密钥

在这里插入图片描述

  • 从上述示例中,我们可以看出,get和put操作涉及的具体操作逻辑如下:
  • get(key):如果key不在缓存中,则返回-1,如果key在缓存中,则返回key所对应的数据值,同时将key设置为最近访问的密钥;
  • put(key,value):看缓存是否达到了容量上限,若达到,则将最近最少使用的密钥对应的数据值删除,写入新的数据值value;若没有达到,则直接写入新的数据值。同时将key设置为最近访问的密钥。
  • 思路1:从上述分析来看,无论是get(key)还是put(key,value)操作,都需要将(key,value)设置为最新访问的内容,所以,我们可以使用链表来存储(key,value)对,即每一个(key,value)对作为链表中的一个结点。
  • 当执行get(key)操作时,将链表中密钥值为key的结点移动到链表的末尾。
  • 当执行put(key,value)操作时,看链表中是否存在密钥为key的结点,若存在删除该结点;若不存在,再看链表长度是否达到缓存容量,如果达到,则删除头节点。最后将(key,value)作为新的尾节点插入到链表。
  • 从上面可以看出,当使用链表作为cache(缓存)的时候,在该数据结构上涉及的主要操作是:
  • (1)将链表中的某个key节点移动到末尾;
  • (2)删除链表的头节点;
  • (3)在链表的末尾插入新的节点;
  • 接下来,我们看看每种操作是的时间复杂度:
  • 操作(1)由于需要在链表中查找密钥值为key的结点,所以时间复杂度为 O ( n ) O(n) O(n),而操作(2)、(3)是链表的插入删除操作,所以时间复杂度为 O ( 1 ) O(1) O(1)
  • 如果要想实现题目要求,那么cache这个数据结构必须具有查找快、插入快、删除快、有顺序之分的特点。
  • 我们知道,哈希表查找快,但是数据无顺序之分;而链表虽然插入快、删除快、并且有顺序之分,但是查找慢,所以结合以下,形成新的数据结构——哈希链表。
  • LRU算法的核心数据结构就是哈希链表,双向链表和哈希表的结合体,具体如下:
  • 在这里插入图片描述
  • 思想很简单,就是借助哈希表赋予链表快速查找的特性:可以快速查找某个key是否在链表中,同时可以快速插入和删除结点。
  • 但可能会有一些问题存在,比如:
  • (1)为什么使用双向链表,单链表不行吗?
  • 主要是因为在链表中插入和删除某个结点的时候,我们需要知道该链表的前一个结点,如果使用单链表,无法在 O ( 1 ) O(1) O(1)时间内完成。
  • (2)既然在哈希表中存储了key值,为什么还要在链表中存储key值;
  • 因为我们删除链表中某个结点的时候,同时还要把哈希表中映射到该结点的key值删除,如果链表中的结点只存储了value值,我们就不知道key值是什么,从而无法删除哈希表中的键。

3、代码实现

class LRUCache:

    def __init__(self, capacity: int):
        

        self.HashDLink = {}
        self.capacity = capacity

        head_key = 'head'
        HeadNode = DLinkNode(head_key,None)

        tail_key = 'tail'
        TailNode = DLinkNode(tail_key,None)

        HeadNode.nex = TailNode
        TailNode.pre = HeadNode

        self.HashDLink[head_key] = HeadNode
        self.HashDLink[tail_key] = TailNode



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

        if key not in self.HashDLink.keys():
            return -1

        curnode = self.HashDLink[key]
        curnode.pre.nex = curnode.nex
        curnode.nex.pre = curnode.pre

        tailnode = self.HashDLink['tail']
        tailnode.pre.nex = curnode
        curnode.pre = tailnode.pre
        curnode.nex = tailnode
        tailnode.pre = curnode

        return self.HashDLink[key].val




    def put(self, key: int, value: int) -> None:
        if self.capacity > 0:

            delnode = None
            if key in self.HashDLink.keys():
                delnode = self.HashDLink[key]
            else:
                if len(self.HashDLink) >= self.capacity + 2:
                    delnode = self.HashDLink['head'].nex
            if delnode != None:
                delnode.pre.nex = delnode.nex
                delnode.nex.pre = delnode.pre
                del self.HashDLink[delnode.key]

            curnode = DLinkNode(key,value)
            tailnode = self.HashDLink['tail']
            tailnode.pre.nex = curnode
            curnode.pre = tailnode.pre
            curnode.nex = tailnode
            tailnode.pre = curnode

            self.HashDLink[key] = curnode




class DLinkNode:

    def __init__(self,key,val):
        self.key = key
        self.val = val
        self.pre = None
        self.nex = None

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值