剑指offer31:最近最少使用缓存

本文详细介绍了如何设计一个最近最少使用(LRU)缓存,要求get和put操作的时间复杂度均为O(1)。LRU缓存使用双向链表结合哈希表实现,当缓存满时,会淘汰最久未使用的键值对。在get和put操作中,都会对涉及的节点进行移动,确保最近访问的节点位于链表尾部。在缓存满时,先删除头节点并从哈希表中移除,然后将新键值对插入。此实现有效地管理了有限的缓存资源并保持高效操作。
摘要由CSDN通过智能技术生成

题目:
请设计实现一个最近最少使用(LRU)缓存,要求如下两个操作的时间复杂度都是O(1)。
1.get(key):如果缓存中存在键key,则返回它对应的值:否则返回-1。
2.put(key,value):如果缓存中之前包含键key,则它的值设为value,否则添加键key以及对应的值value。在添加一个键时,如果缓存已经满了,则在添加新键之前删除最近最少使用的键(缓存中最长时间没有被使用过的元素)
分析:
在这里插入图片描述
a图是一个容量为4的最近最少使用缓存,依次插入四个键值对后,先后在双向链表中插入4个节点。
b图是使用了get方法get(2),该操作访问键2,它的双向链表中的节点被移动到链表的尾部。
c图是put(1,8),更新键1对应的值,这个操作访问键1,所以链表中的节点移动到链表的尾部。
d图是put(5,5),插入一个新的键5,由于此时缓存已经满了,因此在插入新的键之前要把最近最少使用的键3以及对应的值除掉,再把键5添加到哈希表中,同时在链表尾部插入一个新的节点。

代码:

public class ListNode {
    public int val;
    public ListNode next;
    public int key;
    public ListNode prev;
    public ListNode(int key,int val) {
        this.val = val;
        this.key = key;
    }
}
import java.util.HashMap;
import java.util.Map;
//定义最近最少使用缓存的数据结构,缓存中包含一个哈希表,哈希表的键就时缓存的键,哈希表的值就是双向链表中
//的节点
public class LRUCache {
    private ListNode head;
    private ListNode tail;
    private Map<Integer,ListNode> map;
    int capacity;
    public LRUCache(int cap){
        map = new HashMap<>();
        head = new ListNode(-1,-1);
        tail = new ListNode(-1,-1);
        head.next = tail;
        tail.prev = head;
        capacity = cap;
    }
}

import java.util.HashMap;
import java.util.Map;
//定义最近最少使用缓存的数据结构,缓存中包含一个哈希表,哈希表的键就时缓存的键,哈希表的值就是双向链表中
//的节点
public class LRUCache {
    private ListNode head;
    private ListNode tail;
    private Map<Integer,ListNode> map;
    int capacity;
    public LRUCache(int cap){
        map = new HashMap<>();
        head = new ListNode(-1,-1);
        tail = new ListNode(-1,-1);
        head.next = tail;
        tail.prev = head;
        capacity = cap;
    }
    public int get(int key){
        ListNode node = map.get(key);
//        当缓存不存在时,函数get直接返回-1.
        if (node == null){
            return -1;
        }
//        如果缓存包含该键则在返回它对应的值之前先把它在双向链表中对应的值移动到链表的尾部,表示最近访问过该键
        moveToTail(node,node.val);
        return node.val;
    }
    private void put(int key,int value){
        if (map.containsKey(key)){
            moveToTail(map.get(key),value);
        }else {
            if (map.size() == capacity){
                ListNode toBeDeleted = head.next;
                deleteNode(toBeDeleted);
                map.remove(toBeDeleted.key);
            }
            ListNode node = new ListNode(key,value);
            insertToTail(node);
            map.put(key,node);
        }
        
    }
//    该方法就是把双向链表中的一个节点移到链表的尾部,移动一个节点实际上包含两个步骤:首先把它从当前位置删除
//    然后添加到链表的尾部,从双向链表中添加和删除节点,都是通过调整链表中的指针实现的。
    private void moveToTail(ListNode node,int newValue){
        deleteNode(node);
        node.val = newValue;
        insertToTail(node);
    }
    private void deleteNode(ListNode node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    private void insertToTail(ListNode node){
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙崎流河

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

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

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

打赏作者

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

抵扣说明:

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

余额充值