LeetCode || LRU Cache

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

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(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.


即设计一个LRU算法,容器满了之后,有LRU替换规则,开始时我对LRU理解错了,以为需要记录每个元素被访问的次数信息,后来发现不需要,LRU替换的本质是替换掉最长时间没被访问的元素,set和get都算访问操作,故解决方案是cache中的元素是有序的,按照其被访问的时间排序,最新被访问的元素在头部(或尾部),替换时直接删除尾部(或头部)元素即可,如果不使用有序序列的话,那么需要记录每个元素最后被访问的时间,替换时,遍历整个序列,找到时间最久的替换掉,但是这样会超时。故应该保持序列有序。

按照题目要求,要实现get和set功能,为了满足随机存储需求我们首先想到数组,如果用链表会有O(n)的访问时间。然而又要维护 least used 队列,常用的放在前面,用的少的放在后面。这就要求我们要对节点有好的删除和插入操作,这个要求又让我们想到链表,因为数组的删除和插入是O(n)复杂度的。
那么我们能不能维护一个数据结构使得访问操作和插入删除操作都是O(1)复杂度的呢?答案是肯定的。即使用list保存元素,在头部插入,尾部删除,每次访问元素时,把它移动到头部(双向链表中移动一个元素到头部时间复杂度是O(1));再用一个hash_map记录元素的位置。


困难:如何实现在每一次访问元素时,重新调整序列的顺序,让被访问元素移动到头部???

解决:STL中各种容器只有list能方便地调整顺序,就是splice方法,它可以实现把某个list的某个位置的元素或者某个区间位置的元素插入到当前list中,当然如果二者是同一个list,那么就是调整该list中某些元素的顺序了。splice方法的介绍在这里,由于其参数为位置迭代器iterator,故我们需要记录元素key对应的迭代器信息,可以使用map,但是由于map是平衡树实现的有序的序列,故其存取时间复杂度为O(logN),由于我们无需有序,故使用hash_map即可,即unordered_map。


代码如下:

struct node{
    int key;
    int value;
    node(int k, int v):key(k), value(v){}
};

/*
 * 注意整体思路是,使用双向list每次set或get一个元素时都把这个元素放到list的头部,无需统计每个元素的操作次数,实际上LRU的意思
 * 就是根据元素最后被访问的时间来决定替换哪个,故list中尾部元素即被替换.
 * STL技巧:1、使用map的find方法来判断key是否已经存在,返回值和map的end迭代器比较;
            2、使用unordered_map,它是hash_map,存取时间都是O(1),用它存储元素的position迭代器,是为了方便splice函数调用
 *          list.splice(position, list, element_pos)函数作用是把list的element_pos处的元素插入到position位置,本题中
            为了移动元素到list头部
 */
class LRUCache{
    int size;
    list<node> values;
    unordered_map<int, list<node>::iterator> positions;
public:
    LRUCache(int capacity) {
        size = capacity;
    }
    
    int get(int key) {
        if(positions.find(key) != positions.end()){
            values.splice(values.begin(), values, positions[key]);
            positions[key] = values.begin();
            
            return values.begin()->value;
        }
        return -1;
    }
    
    void set(int key, int value) {
        if(positions.find(key) != positions.end()){
            values.splice(values.begin(), values, positions[key]);  //移动被访问元素到头部
            values.begin()->value = value;
            positions[key] = values.begin();  //更新其位置,注意此处的position只是一个指针,当此key在list中被挤到其他位置后,positions里保存的位置也会跟着变化,因为它仅仅是一个指向该结点的指针       
        }
        else if(values.size()<size){
            values.push_front(node(key, value));
            positions[key] = values.begin();
        }
        else{
            node last = values.back();
            values.pop_back();
            positions.erase(last.key);
            
            values.push_front(node(key, value));
            positions[key] = values.begin();
        }
    }
    
};


参考链接:

http://www.myexception.cn/program/1598814.html

http://www.cnblogs.com/x1957/p/3485053.html





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值