LRU Cache

406 篇文章 0 订阅
406 篇文章 0 订阅

1,题目要求

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value)- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

设计并实现最近最少使用(LRU)缓存的数据结构。 它应该支持以下操作:get和put。

get(key) - 如果密钥存在于缓存中,则获取密钥的值(将始终为正),否则返回-1。
put(key,value) - 如果该键尚不存在,则设置或插入该值。 当缓存达到其容量时,它应该在插入新项之前使最近最少使用的项无效。

补充:
你可以在O(1)时间复杂度下进行这两项操作吗?

2,题目思路

对于这道题,题目是实现一个最近最少使用的缓存数据结构并实现get和put的操作。

题目的主要要求,就是实现一种数据操作,即我们可以根据key值,在对应的数据结构中读取;并且向其中添加新元素,如果该元素在数据结构中不存在,我们就直接加入。如果该元素已经在数据结构汇总出现了,我们就将其设置最近访问的元素。如果此时容器满了,就将最不常用的元素删除。

因此,这里涉及到的,就是一种访问频率问题。
直观的去想,我们可以为每个元素设置一个访问属性,比如,每次对容器的访问,都可以设置为一个时间片。如果当前元素没有被访问,就对访问频率属性进行一个累加,每次访问到的元素,就设置其访问频率归零。当我们需要删除元素时,就将当前访问频率值最大的元素删除。

这样虽然是可行的,但是每次的插入、删除、更新频率操作,都需要O(n)的时间复杂度,不满足O(1)的要求。

因此,在这个问题的实现上:
我们定义一个used,容器为list,用来保存当前访问的元素,其中,这个list的值为题目对应的key,而这些key在used中的位置(迭代指针所指的位置)作为频率的表示,front为最近访问的,back最久未访问的。

然后,我们定义一个cache,容器为一个unordered_map,用来保存输入的数据:其中,key是题目中对应的key,而value是一个pair,对于这个pair而言:
first是题目给定的value
second是一个迭代器(指针),表示这个元素在used中出现位置,方便我们后续的操作

最后,我们定义一个update函数,用来对新加入的元素或者重新访问的元素的频率属性进行更新。
其实,新加入的元素,就相当于最近访问的元素。

具体的实现和操作,见第三部分的代码及注释。

3,代码实现

static const auto s = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    return nullptr;
}();

class LRUCache {
public:
    LRUCache(int capacity) {
        _capacity = capacity;
    }
    
    int get(int key) {
        auto it = cache.find(key);
        if(it == cache.end())
            return -1;  //没有找到对应的元素
        update(it); //更改为最近访问
        return it->second.first;    //返回key对应的value
        
    }
    
    void put(int key, int value) {
        auto it = cache.find(key);
        if(it != cache.end())   //key在cache中
            update(it); //将其改为最近访问
        else{
            if(cache.size() == _capacity){
                cache.erase(used.back());   //删除最后一个、即最不经常用的元素
                used.pop_back();
            }
            used.push_front(key);
        }
        //因为每次加入元素,都会让该元素成为最近使用的
        //因此,我们也就将对应的{value, 当前元素在used中位置(指针)}设置cache中key对应的value
        cache[key] = {value, used.begin()};
    }
private:
    //用于表示used
    typedef list<int> LI; 
    
    //用于表示cache中的value属性
    //是cache与used之间联系的桥梁
    //key:对应题中的value;
    //value:key对应的数据在used中的存储位置
    typedef pair<int, LI::iterator> PII;    
    
    //用于表示cache
    //key:对应题中的key
    //value:是一个pair,其中first是题中给出的value值,而second是个指针,表示其在used中出现的位置,方便进行更新操作
    typedef unordered_map<int, PII> HIPII;  
    
    //定义一个cache,用于保存加入的数据
    HIPII cache;
    
    //保持一个list,用来表示元素的访问最近访问属性
    //list的值为题中给定的key
    //其中front为最近访问的,back是最不常访问的
    LI used;    
    int _capacity;
    
    //更改访问到的节点的“最近访问属性”
    void update(HIPII::iterator it){
        int key = it->first;
        //删除当前元素在used中的占位信息
        //需要注意的是,erase是通过指针进行删除的
        //这也是我们保持iterator的原因
        used.erase(it->second.second);
        used.push_front(key);   //将该元素设为新的最近使用元素
        //以指针指向的位置作为cache与used之间的联系,
        //方便在加入、重新访问该元素对“最近访问属性”进行修改
        it->second.second = used.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
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值