码迷,mamicode.com
首页 > 系统相关 > 详细

LeetCode-LRU Cache

时间:2015-07-28 18:36:30      阅读:174      评论:0      收藏:0      [点我收藏+]

标签:缓存   结构   map   lru   算法   

链接:http://oj.leetcode.com/problems/lru-cache/

参考:http://www.acmerblog.com/leetcode-lru-cache-lru-5745.html


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.

    这是比较经典的替换算法,在虚拟内存跟缓存策略中有应用,根据数据的历史访问记录来淘汰过老数据。

    其策略是替换没有被访问过的最近的“如果数据最近被访问过,那么将来被访问的几率也更高”。 一般应用在缓存替换策略中。其中的”使用”包括访问get和更新set。

LRU算法

     LRU是Least Recently Used 最近最少被使用算法,页面调入内存后的使用情况进行决策的。由于无法预测各页面将来的使用情况,只能利用“最近的过去”作为“最近的将来”的近似,因此,LRU置换算法是选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间t,当须淘汰一个页面时,选择现有页面中其t值最大的,即最近最久未使用的页面予以淘汰(可以使用这种方法去实现)。对于在内存中但又不用的内存块,Oracle会根据那些数据属于LRU而将其移出内存而腾出空间来加载另外的数据,一般用于大数据处理的时候很少使用的数据那么就直接请求数据库,如果经常请求的数据就直接在缓存里面读取。

     本题考虑实现get,set 两个方法,实际上要记录一定capacity 的key - value 对,类似redis。

分析:为了保持cache的性能,使查找,插入,删除都有较高的性能,我们使用双向链表(std::list)和哈希表(std::unordered_map)作为cache的数据结构,因为:

  • 双向链表插入删除效率高(单向链表插入和删除时,还要查找节点的前节点)
  • 哈希表保存每个节点的地址,可以基本保证在O(1)时间内查找节点

具体实现细节:

  • 越靠近链表头部,表示节点上次访问距离现在时间最短,尾部的节点表示最近访问最少
  • 查询或者访问节点时,如果节点存在,把该节点交换到链表头部,同时更新hash表中该节点的地址
  • 插入节点时,如果cache的size达到了上限,则删除尾部节点,同时要在hash表中删除对应的项。新节点都插入链表头部。     

    可以用map实现  key -value 对存储,由于key无序采用unorded_map性能更高,速度更快(实际上也可以用map 或者 手写红黑树,但是做题太费时间,不安全,况且BST最多O(LOG N) 不能O(1) )。

    这样虽然存储问题解决了,但是时间没法比较,故应该使用链表,前面的是新的,每次插入head,满了空间除掉尾。

    双向链表用起来方法多点,故用list。为了索引key是否在链表中,list自带find 复杂度O(N)

    故用unorded_map实现  key ->  iterator  对的存储,这样查找key  复杂度 O(log n)


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

class LRUCache{
public:
    LRUCache(int capacity) {
        size = capacity;
    }
    
    int get(int key) {
        if(cacheMap.find(key) == cacheMap.end())
            return -1;
        else
        {
            //把当前访问的节点移到链表头部,并且更新map中该节点的地址
            cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
            cacheMap[key] = cacheList.begin();
            return cacheMap[key]->value;
        }
        
    }
    
    void set(int key, int value) {
        if(cacheMap.find(key) == cacheMap.end())
        {
            if(cacheList.size() == size)
            {//删除链表尾部节点(最少访问的节点)
                cacheMap.erase(cacheList.back().key);
                cacheList.pop_back();
            }
            //插入新节点到链表头部,并且更新map中增加该节点
            cacheList.push_front(CacheNode(key, value));
            cacheMap[key] = cacheList.begin();
        }
        else
        {//更新节点的值,把当前访问的节点移到链表头部,并且更新map中该节点的地址
            cacheMap[key]->value = value;
            cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
            cacheMap[key] = cacheList.begin();
        }
        
    }
private:
    list<CacheNode> cacheList;
    unordered_map<int, list<CacheNode>::iterator> cacheMap;
    int size;
};


版权声明:本文为博主原创文章,未经博主允许不得转载。

LeetCode-LRU Cache

标签:缓存   结构   map   lru   算法   

原文地址:http://blog.csdn.net/jonathanxqs/article/details/47103989

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!