LRU Cache

来源:互联网 发布:linux中的vi指令 编辑:程序博客网 时间:2024/05/16 12:54

Problem description:

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.

分析

1)对于get方法,考虑到是cache,速度要求非常高,应该在O(1)时间返回结果,应该用到hash来存储,也就是hash_map或unordered_map。

2)更新策略是LRU,那么就需要知道存储的每个键的实效信息,最简单的我们可以设置一个这样的字段,来记录实效信息,设计map结构为:unordered_map<int, map<int, int> >,三个类型依次表示为key,实效,value,但是问题出来了,假设在插入新的元素时已经达到上限,则要求进行替换,此时为了获得实效信息,我们不得不选择遍历来找到应该被替换的元素,这对cache角色而言是无法容忍的。

到这,问题就很清晰了,就是怎么样才能在O(1)时间确认哪个该被替换,各位女看官应该很有经验。想想自己的衣柜,总是新买的衣服放在最上边,最近经常穿的放在最上边,那么就是队列了,我们将新的元素放在队列首,访问过的元素也放在队首,那么即不是新插入的,最近也一直没被访问的,就该是要被替换的了,这个元素很明显就在队尾了(一直都没穿的肯定就是压箱底的了)。

对于这个队列,当然可以是语言本身提供的,也可以是我们自己定义的双向链表,现在问题又来了,我的链表结点中定义哪些字段呢?同时map中key是很明显的,那相应的value要是什么呢?

这里我们以双向链表结构说明。

我们将链表结构设计如下:

struct Node{int key;int value;struct Node *pre;struct Node *next;};
map结构定义为:

unordered_map<int, Node*> mp;
这样一来,其实mp中的key作为Node结构中真实存储<key,value>对的索引,通过其对应的Node*指针来影射到真实存储信息的链表结点。这样,在查询key的操作中,我们可以在O(1)时间完成,同时,在替换策略上,我们也可以在O(1)时间内确定具体的替换结点,很简单,我们可以设定一个head和一个tail指针,head来处理插入,tail来处理删除,同时,在中间位置存储的元素,由于被访问要插入到头部的时候,由于是双向链表,其操作也非常简单也是O(1)。如此一来,所有的问题基本都解决了,接下来就是具体的实现,在实现细节上,会出现一些问题,这些问题对不同水平的人都不相同,向作者这样的菜鸟,出来一大堆的错误就很正常了,不过没关系,可以修改,可以学习,就可以进步。

//代码:

class LRUCache{public:struct Node{int key;int value;struct Node *pre;struct Node *next;Node(int k = 0, int v = 0):key(k), value(v), pre(NULL), next(NULL){}};LRUCache(int c = 0):capacity(c){//assert(c > 1);if(c < 1) return;head = new Node(0, 0);tail = new Node(0, 0);head->next = tail;tail->pre = head;size = 0;mp.clear();}void AddToHead1(Node *p)//first cut the relationship and build new relationship.{//break away from the old chaining.p->next->pre = p->pre;p->pre->next = p->next;//insert at the front. head->next->pre = p;p->next = head->next;//notice the order.p->pre = head;head->next = p;}void AddToHead2(Node *p)//new node, build relationship directly.{//insert at the front.head->next->pre = p;p->next = head->next;//notice the order.p->pre = head;head->next = p;}void RemoveTail(){//pre<>del<>tail,==> pre <> tailNode *pdel = tail->pre;tail->pre = pdel->pre;pdel->pre->next = tail;unordered_map<int, Node *>::iterator dit = mp.find(pdel->key);mp.erase(dit);//delete from map.delete pdel;  }int get(int key){unordered_map<int,Node*>::iterator mit = mp.find(key);if(mit != mp.end()){Node * pfind = mit->second;AddToHead1(pfind);return pfind->value;}return -1;}int set(int key, int value){if(capacity < 1) return 0;unordered_map<int, Node*>::iterator mit = mp.find(key);if(mit != mp.end())//find {Node * pfind = mit->second;pfind->value = value;AddToHead1(pfind);}else//not find{Node *pnew = new Node(key, value);AddToHead2(pnew);mp[key] = pnew;if(size < capacity) ++size;else{RemoveTail();}}return 0;}private:unordered_map<int, Node*> mp;Node *head;Node *tail;int size;int capacity;};

程序虽然AC了,但肯定也还存在其他的问题,各位看官要是觉得作者哪里写的比较晦涩,请留言,定会尽早的回复您,当然若发现问题,更是欢迎不吝指教。



0 0
原创粉丝点击