【高阶数据结构】LRU Cache

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.什么是LRU Cache

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。 广义上的Cache指的是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache, 内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache── 称为Internet临时文件夹或网络内容缓存等。

在这里插入图片描述

Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。

在这里插入图片描述

2.LRU Cache的实现

下面借助oj题来实现。

题目链接:146. LRU 缓存

在这里插入图片描述

LRU Cache最重要的就是两个操作put和get,它要求必须以 O(1) 的平均时间复杂度运行。

还有这道题LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存,超过capacity就满了,实际上可能要按照字节来算。

get就是给一个关键字但是实际上是地址,看这个地址在不在,在的话就是在缓存,然后去缓存在去取这块的数据。

put有两种场景,有可能是新增,有可能是更新,如果关键字在了就是更新,不在就是新增。如果插入的时候导致关键字数量capacity,就把最近最少使用的删除。·

那如何设计出这里的结构呢?

大部分看到put和get涉及查找并且要求O(1),可能会想到用哈希表。unordered_map<int,int> _hashMap,这样查是O(1),更新O(1),新增O(1)。

但是真正要考虑的是LRU,满了删谁。谁是最近最少用的,map能帮查找查是O(1),更新O(1),新增O(1),如何保证LRU?

LRU涉及顺序的问题。那个数据结构可以满足顺序吗?
vector和list都可以。

这里我们用的是list控制LRU,list<pair<int,int>> _LRUlist,如何控制其实很简单,在_LRUlist里面默认认为尾巴的是最近最少使用,只要一个数据被用了就提到队头,如果在插入一个数据满了,就把尾巴上的那个最近最少使用的删除。

在这里插入图片描述

但是unordered_map<int,int> 和 list<pair<int,int>> 结合起来并不是所有操作都能做到O(1)。get查map可以做到O(1),put新增是O(1),更新是O(n),因为我们的数据在map存一份在list存一份,新增map是O(1),list头插也是O(1),虽然更新map是O(1),然后要调整_LRUlist中key的位置到头部,更新顺序,但是我们不知道数据在哪,只能遍历去查,这个就是O(n)。只要访问过的数据都要往队头提!

那怎么做呢?

找到key,就要找到key对于存储数据在list中位置。

unordered_map 的 value给一个list的迭代器。指向对于key存储数据在list中位置,这样查找,新增,更新都是O(1) 都先去unordered_map 查key然后在做对应操作。

在这里插入图片描述

list还有一个接口splice(接合),可以不用保存值删节点然后重新new插入头,而是直接把节点转移队头。

在这里插入图片描述

class LRUCache 
{
public:
    LRUCache(int capacity) 
        :_capacity(capacity)
    {}

    int get(int key) 
    {
        auto ret = _hashMap.find(key);
        if (ret != _hashMap.end())
        {
            //更新key对应值的位置
            LtIter it = ret->second;
            //1. erase + push_front + 更新迭代器,原迭代器失效
            //2. splice接合,转移节点
            _LRUlist.splice(_LRUlist.begin(), _LRUlist, it);
            return it->second;
        }
        else
        {
            return -1;
        }
    }

    void put(int key, int value) 
    {
        //1.新增
        //2.更新
        auto ret = _hashMap.find(key);
        if (ret == _hashMap.end())
        {
            //满了,先删除LRU的数据
            if (_capacity == _hashMap.size())
            {
                auto back = _LRUlist.back();
                _LRUlist.pop_back();
                _hashMap.erase(back.first);
            }
            //插入
            _LRUlist.push_front(make_pair(key, value));
            _hashMap[key] = _LRUlist.begin();
        }
        else
        {
            //更新key对应值的位置
            LtIter it = ret->second;
            //更新
            it->second = value;
            _LRUlist.splice(_LRUlist.begin(), _LRUlist, it);
        }
    }

private:
    typedef list<pair<int, int>>::iterator LtIter;
    //hash做到查找更新是O(1)
    unordered_map<int, LtIter> _hashMap;

    //LRU 假设尾部数据就是最近最少使用
    list<pair<int, int>> _LRUlist;
    size_t _capacity;

};

/**
 * 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);
 */
  • 30
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
LRU Cache是一种常见的缓存淘汰策略,LRU代表最近最少使用。在Java中,可以使用不同的方式来实现LRU Cache。 引用\[1\]中的代码展示了一种自定义的LRU Cache实现,使用了一个自定义的LRUCache类,并在main方法中进行了测试。在这个实现中,LRUCache类继承了LinkedHashMap,并重写了removeEldestEntry方法来实现缓存的淘汰策略。 引用\[2\]中的代码展示了另一种自定义的LRU Cache实现,同样使用了一个自定义的LRUCache类,并在main方法中进行了测试。这个实现中,LRUCache类同样继承了LinkedHashMap,并重写了removeEldestEntry方法来实现缓存的淘汰策略。 引用\[3\]中的代码展示了使用ArrayList实现LRU Cache的方式。在这个实现中,LRUCache类使用了一个ArrayList来存储缓存数据,并通过get和put方法来操作缓存。 总结来说,LRU Cache的Java实现可以使用自定义的类继承LinkedHashMap并重写removeEldestEntry方法,或者使用ArrayList来存储缓存数据。具体的实现方式可以根据需求和偏好选择。 #### 引用[.reference_title] - *1* *2* [Java——LRUCache](https://blog.csdn.net/m0_60867520/article/details/128361042)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [LRUCache的Java实现](https://blog.csdn.net/qq_39383118/article/details/103535985)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值