leetcode-146. LRU缓存机制

本文详细介绍了一种高效实现LRU(最近最少使用)缓存机制的方法,利用Java中的LinkedHashMap数据结构,实现了O(1)时间复杂度的get和put操作。通过继承并覆盖LinkedHashMap中的方法,构造了一个符合LRU策略的缓存,有效管理和更新缓存中的数据。

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

不用那么多花里胡哨,直接12行代码解决 /苦笑

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?。

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

 

首先写下LRU算法的理解:LRU即最久未使用淘汰策略,最常用于redis key-value 淘汰策略
在redis中使用一个队列来维护所有键值对集合,当一个key被get/put时这个键值对会被移动到最顶端,
这样末端的键值对就成了最久未使用的
 java中实现这个队列最简单的方式还是使用双向链表实现,查询就需要用到Hash表了
 最合适的数据结构,例如~~~~   LinkedHashMap
 为什么选择它呢:
    第一:Java中get/put/remove 时间复杂度达到 O(1)的就只有Map接口下面的实现类了
    第二:LinkedHashMap是HashMap的子类并且它是有序的,那么这个队列就形成了
    第三:该看看源码了,LinkedHashMap中有类似的相关方法的实现,
          我们只需要继承并覆盖其中需要改动的部分就可以了

其作用就是在访问元素之后,将该元素放到双向链表的尾巴处
(所以这个函数只有在按照读取的顺序的时候才会执行),
建议大家去看看,如何优美的实现在双向链表中将指定元素放入链尾!
 void afterNodeAccess(Node<K,V> p) { }
 
其作用就是在删除元素之后,将元素从双向链表中删除
 void afterNodeRemoval(Node<K,V> p) { }
 
是否删除最久未使用节点
 void afterNodeInsertion(boolean evict) { }

 在插入新元素之后,需要回调函数判断是否需要移除一直不用的某些元素!
 移除最近最少被访问条件之一,通过覆盖此方法可实现不同策略的缓存
 LinkedHashMap是默认返回false的,我们可以继承LinkedHashMap然后复写该方法即可
 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
 return false;
 }

题解: 由于继承了LinkedHashMap,并且不需要对put方法进行修改,所以不需要写put方法

class LRUCache extends LinkedHashMap<Integer, Integer>{

    // 容器容量
    private int capacity;

    // 初始化LinkedHashMap设置初始容量和加载因子
    public LRUCache(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    // 修改get方法,当key-value不存在时返回-1
    @Override
    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    // 修改回调方法,返回true时会删除末尾节点,所以用size和容量比较
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}

这个故事告诉我们,一定要多看源码!!!

其实redis中还有一个LRU的升级版,使用两个缓存队列,就不在这里扯了,感兴趣的可以去搜一搜。

 


参考作者:jeromememory
链接:https://leetcode-cn.com/problems/lru-cache/solution/yuan-yu-linkedhashmapyuan-ma-by-jeromememory/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值