146. LRU Cache

来源:互联网 发布:ansys软件报价 编辑:程序博客网 时间:2024/04/29 20:41

题目大意:设计一个用于LRU cache算法的数据结构。关于LRU的基本知识可参考here

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

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

具体实现细节:

  • 越靠近链表头部,表示节点上次访问距离现在时间越短,尾部的节点表示很长时间已经没被访问了
  • 查询或者访问节点时,如果节点存在,把该节点弄到链表头部,同时更新hash表中该节点的地址
  • 插入节点时,如果cache的size达到了上限,则删除尾部节点,同时要在hash表中删除对应的项。新节点则插入链表头部
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;};
2015.9.20更新
----------------
思路:

LRU cache数据结构的核心就是当存储空间满了,而有新的item要插入时,要先丢弃最早的item。这里的数据结构需要符合以下条件:

1. 要能快速找到最早更新的item。这里需要将item以更新时间顺序排序。
可选的有:queue(队列),heap(优先级队列),linked list(链表)

2. 要能快速访问指定item,并且访问以后要更新它的时间顺序。
对于更新时间顺序这个操作,queue和heap要做到就很困难了。所以这点最佳的是linked list。但linked list中查找指定item需要遍历,这里可以用一个hash table来记录key与节点之间的对应。并且由于要随时更新节点位置,doubly linked list更为适用。
还有一点需要注意的是,当把一个新元素A插入到list前面的时候,我们在unordered_map中保存新元素A对应的iterator(也就是list.begin())。当继续插入其他元素B,C,D进入list前面时,unordered_map中保存的A元素对应的iterator并没有失效!!!切记!
代码如下:
class LRUCache{private:    struct cacheNode {        int key;        int value;        cacheNode(int k, int v): key(k), value(v) {}    };    unordered_map<int, list<cacheNode>::iterator> uMap;    list<cacheNode> lst;    int cap;public:    LRUCache(int capacity) {        cap = capacity;    }        int get(int key) {        if (uMap.count(key)==0) {            return -1;        } else {            lst.splice(lst.begin(), lst, uMap[key]);            uMap[key] = lst.begin();            return uMap[key]->value;        }    }        void set(int key, int value) {        if (uMap.count(key)) {            lst.splice(lst.begin(), lst, uMap[key]);            uMap[key] = lst.begin();            uMap[key]->value = value;        } else {            if (uMap.size()==cap) {                uMap.erase(lst.back().key);                lst.pop_back();                lst.push_front(cacheNode(key, value));                uMap[key] = lst.begin();            } else {                lst.push_front(cacheNode(key, value));                uMap[key] = lst.begin();            }        }    }};




0 0
原创粉丝点击