LRU Cache

本文解析了如何使用C++的list数据结构,配合unordered_map实现线程安全的LRU缓存,以实现在put和get操作中的O(1)时间复杂度,重点讲解了如何维护最近使用顺序和快速删除/插入元素的技术细节。
摘要由CSDN通过智能技术生成

LRU Cache

Question:

请添加图片描述

思路:

这道题呢, 其实难点就在这个他要求put 和 get 都是O(1), 因为其实如果没有效率的要求, 那这个题就随随便便套用一个unordered_map, 然后胡乱更新就完了.
但他要求了要O(1), 所以说就是我们还要想一个方法,实时track 这个least recently used key.
一个比较直观的方法就是, 我们设置一个container, 然后每次运用到一个key,就把这个key 推到container的最前方, 每次当put需要evict的时候, 我们只要pop掉这个container 里的最后一项就可以了. 因为container里的最后一项肯定就是least recently used key.

然后我们要想一下怎么构建这个container 才能使得我们可以pop 最后一项,然后每次都push到前面. 并且update这个container 必须是O(1).
首先光是满足存储key的container, 我们可以用array, vector, stack or queue. 但是他们几个都没发满足O(1). 因为 我们思考一下. 首先光是evict least recently used key, 这几个container 就都满足不了。array的话 当删掉一个item, 就会多出一个空白, 然后我门需要把array里其他的所有item都调整一个位置. 同理当有新的key被用到, 那么其他所有item都要往后挪一下(以来代表新的key 是最recently used, 其他都是更least recently used), 没法满足O(1). 其次退一步想, 如果我们想实现O(1) access, 我们必须得有一个pointer 来指向对应的key 和 他的顺序,是排第几recently used.但是我们的array 是keep chaning 的, 一个pointer 没法随着array的变化一直指向同一个item. pointer可以指向array里的一个固定位置. 但是当array里的item 变了顺序之后, pointer也得重新指向才能 指到corresponding item.
stack vector queue 同理.
vector 首先erase 就不是O(1), 其次pointer 只能指向vector的某一个位置的address, 并不能随着里面内容的改变pointer跟着变,意思就是每次更新完vector,我还得重新跟新一下pointer的指向.
stack 和 queue 都不支持random access, 就是stack[0], queue[1] 这种都用不了,那就更别提什么用pointer实时更新了. 没办法把stack 和 queue 里中间的item 进行更新.

有一个container: deque 是支持random access 的,但deque 和vector 差不多, 好像比vector 更高级
(待以后需要更细节的学习)

最后揭晓答案: 我们用c++ 的 list.
c++ 的 list 本质上是一个double linked list.
linked list的好处不必多说, pointer可以一直指向同一个地址, 不用担心增加item之后同一个地址会变成不同的item. 指向的某一个item 永远存在一个地址下.
删除least recent 和增加一个新的到front 都可以在O(1) 内完成, 因为是double linked list, 所以可以直接O(1) 删除一个链接或者增加一个链接.
完美

然后key idea 就是我们要先 做一个container “list” 存recent use order, 然后每次cache里加新东西的时候不仅要把key 存在cache 里, 也要把新的key 加进list里来记录顺序,

所以就是我们还用一个hash map 存key-value pair. value的话我们还存一个pair, value = <value, key的order> 然后key 的order 我们可以选择存一个pointer 指向这个在"list" 里面的key. 这样的话, 一来每次往list加新东西的时候, 我们存的pointer 指向的key 是不会变的. 二来把pointer 存在hash map 里面,可以保证了O(1). 这样每次put 一个existing key 的时候, 更新"list"里的order 就可以直接从hash map 的value 这里 通过先前存好的pointer找到对应在"list" 里的key, 然后可以直接删除和更新.

因为"list" 是自带iterator 的 ADT, 所以我就用iterator 代替pointer 存在hash map 里了.

写了一大堆。 上code. 应该看完这么多解释可以理解code了

class LRUCache {
    int capacity;
    list<int> order;
    unordered_map<int, pair<int, list<int>::iterator>> cache;
    
public:
    LRUCache(int capacity) {
        this->capacity = capacity;

    }
    
    int get(int key) {
        if(cache.find(key) == cache.end()){
            return -1;
        } else {
            int val = cache[key].first;
            this->order.erase(cache[key].second);
            this->order.push_front(key);
            this->cache.at(key).second = this->order.begin(); //每一次存begin 是因为我门
            //每一次都加到最前面, 而这个最前面是一个link list的node. 指向他的iterator 
            //会一直指向这个node,不会因为下一次增加了新的node 而指向新的begin的node。  
            // 意思就是这里的begin 只代表当前的begin 不是永远指向 order这个list的begin。
            return val;
        }
    }
    
    void put(int key, int value) {
        if(this->cache.find(key) == this->cache.end()){
            if(this->cache.size() == this->capacity){
                int back = this->order.back();
                this->order.pop_back();
                this->cache.erase(back);
                
            }
            this->order.push_front(key);
            this->cache[key] = {value, this->order.begin()};
        } else {
            this->order.erase(cache[key].second);
            this->order.push_front(key);
            this->cache[key] = {value, this->order.begin()};
        }
    }
};

/**
 * 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);
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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 ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值