链接: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的数据结构,因为:
具体实现细节:
可以用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; };
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/jonathanxqs/article/details/47103989