LeetCode Hot 100 No.146 LRU缓存机制

本文介绍了一种使用双向链表和哈希表实现LRU(最近最少使用)缓存淘汰策略的方法。通过维护一个双向链表和哈希表,确保了缓存更新和查找操作的高效性。当缓存容量达到上限时,会自动移除最近最少使用的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
思路:
首先我们用一个双向链表来作为缓存区。这个双向链表list需要有一个头指针head,一个尾指针last,和一个记录当前长度的变量length。

这个双向链表的结点Node有一个指向上一结点的指针last, 还有一个指向下一结点的指针next,还有一个保存当前节点键的变量key,一个保存当前节点键值的变量val。

还有,我们要建立一个hashmap来保存当前缓存区中保存的节点的信息。<Integer, Node> 分别对应着每个节点的键和该节点的引用。

我们来分析一下,LRU的具体操作该如何用这两个数据结构来实现。

首先,当我们往缓存中put一个关键字<key,value>:

  1. 我们首先在hashmap中通过key来找这个节点是否存在。

  2. 如果存在,则修改hashmap中的值,并将该节点移动到list的头部。

  3. 如果不存在,则新建一个节点。我们还要判断一下,当前list是否已满,如果已满,我们就删除list尾端的节点,并且还要将尾部节点从hashmap中删除掉。然后将新节点插入到list的头部,并且将它加入hashmap。

然后我们来看get操作:

  1. 我们首先在hashmap中查看该节点是否存在。
  2. 如果不存在,那么直接返回-1。
  3. 如果存在,那么我们就将该节点移动到list的头部,并且返回该节点的val。

分析完了上面的两个操作,我们发现最核心的操作其实只有三个:一个是将某个节点直接插入list的头部,一个是将某个节点从list中删除,一个是将某个节点移动到list的头部。而且最后一个操作可以分解为,先将这个节点从list中删除,再将这个节点插入到list的头部。

那么,我们的双向链表的主要操作只有两个,一个是将某个节点插入到list的头部,一个是将某个节点从list中删除。

这里要注意一点,双向链表中,头指针和尾指针需要一直指向一个伪头节点和一个伪尾节点。这两个节点不保存任何信息。这么做的目的是始终不改变head和last的值,让插入和删除操作始终都在头结点和尾节点的中间进行,非常巧妙的减少了很多判断操作。

class LRUCache {
    private class Node
    {
        Node last;
        Node next;
        int key;
        int val;
        public Node(int key,int val)
        {
            this.key = key;
            this.val = val;
            this.last = null;
            this.next = null;
        }
    }
    private class TowWayLL
    {
        Node head;
        Node last;
        int length;

        public TowWayLL()
        {
            this.head = new Node(0,0);//添加伪头部结点
            this.last = new Node(0,0);//添加伪尾部结点
            head.next = last;
            last.last = head;
            this.length = 0;
        }
        public Node remove(Node n)
        {
                this.length--;
                n.next.last = n.last;
                n.last.next = n.next;
                return n;
        }
        public void putTohead(Node n)//将该节点加入到头部
        {
            n.next = head.next;
            n.last = head;
            head.next.last = n;
            head.next = n;
            this.length++;
        }
        public void moveTohead(Node n)//将该节点移动到头部
        {
            this.remove(n);
            this.putTohead(n);
        }

        

    }


    HashMap<Integer,Node> map;;
    TowWayLL list ;
    int capacity;
    public LRUCache(int capacity) {
       map = new HashMap<>();
       list = new TowWayLL();
        this.capacity = capacity;
        // System.out.println("capacity "+this.capacity);
    }
    
    public int get(int key) {
        // System.out.println("get "+key);
        Node n  = map.getOrDefault(key,null);
        if(n==null)
            return -1;
        else
        {
            list.moveTohead(n);
            return n.val;
        }

    
    }
    
    public void put(int key, int value) {
        // System.out.println("put "+key+" "+value+" length "+ list.length + "capacity"+capacity);
        Node n  = map.getOrDefault(key,null);
        if(n!=null)//如果结点已经存在,则先修改结点的value,再将该节点移动到list头部
        {
            n.val = value;
            list.moveTohead(n);
        }
            
        else//如果结点不存在
        {
            n = new Node(key,value);//先新建结点
            if(list.length==capacity)//如果当前缓存已满,则删除缓存队列中的最后一个,并将map中对应结点删除
            {
                Node last = list.remove(list.last.last);
                map.remove(last.key);
            }
            list.putTohead(n);//将新节点插入头部
        }
        map.put(key,n);//最后将该节点重新放入map中

    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值