标签:
1. FIFO -- 先进先出
如果一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。
实现:
利用一个双向链表保存数据,当来了新的数据之后便添加到链表末尾,如果Cache存满数据,则把链表头部数据删除,然后把新的数据添加到链表末尾。在访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值;否则返回-1。如果想提高访问效率,可以利用hashmap来保存每个key在链表中对应的位置。
2. LFU -- 最近最少使用
基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。
LFU是基于访问次数的。
实现:
为了能够淘汰最少使用的数据,LFU算法最简单的一种设计思路就是利用一个数组存储数据项,用hashmap存储每个数据项在数组中对应的位置,然后为每个数据项设计一个访问频次,当数据项被命中时,访问频次自增,在淘汰的时候淘汰访问频次最少的数据。这样一来的话,在插入数据和访问数据的时候都能达到O(1)的时间复杂度,在淘汰数据的时候,通过选择算法得到应该淘汰的数据项在数组中的索引,并将该索引位置的内容替换为新来的数据内容即可,这样的话,淘汰数据的操作时间复杂度为O(n)。
另外还有一种实现思路就是利用小顶堆+hashmap,小顶堆插入、删除操作都能达到O(logn)时间复杂度,因此效率相比第一种实现方法更加高效。
3. LRU -- 最近最久未使用
如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
实现:
(1)用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。
思路简单,但是需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。
(2)利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部;如果不存在,则新建一个节点,放到链表头部。若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。
在已知要删除的节点的情况下,如何在O(1)时间复杂度内删除节点?
假如要删除的节点是cur,通过cur可以知道cur节点的后继节点curNext,如果交换cur节点和curNext节点的数据域,然后删除curNext节点(curNext节点是很好删除地),此时便在O(1)时间复杂度内完成了cur节点的删除。
如何使得删除末尾节点的复杂度也在O(1)?
利用双向链表,并提供head指针和tail指针,这样一来,所有的操作都是O(1)时间复杂度。
参考实现:
(1)
#include <iostream> #include <map> #include <algorithm> using namespace std; struct Node { int key; int value; Node *pre; Node *next; }; class LRUCache{ private: int count; int size ; map<int,Node *> mp; Node *cacheHead; Node *cacheTail; public: LRUCache(int capacity) { size = capacity; cacheHead = NULL; cacheTail = NULL; count = 0; } int get(int key) { if(cacheHead==NULL) return -1; map<int,Node *>::iterator it=mp.find(key); if(it==mp.end()) //如果在Cache中不存在该key, 则返回-1 { return -1; } else { Node *p = it->second; pushFront(p); //将节点p置于链表头部 } return cacheHead->value; } void set(int key, int value) { if(cacheHead==NULL) //如果链表为空,直接放在链表头部 { cacheHead = (Node *)malloc(sizeof(Node)); cacheHead->key = key; cacheHead->value = value; cacheHead->pre = NULL; cacheHead->next = NULL; mp[key] = cacheHead; cacheTail = cacheHead; count++; } else //否则,在map中查找 { map<int,Node *>::iterator it=mp.find(key); if(it==mp.end()) //没有命中 { if(count == size) //cache满了 { if(cacheHead==cacheTail&&cacheHead!=NULL) //只有一个节点 { mp.erase(cacheHead->key); cacheHead->key = key; cacheHead->value = value; mp[key] = cacheHead; } else { Node * p =cacheTail; cacheTail->pre->next = cacheTail->next; cacheTail = cacheTail->pre; mp.erase(p->key); p->key= key; p->value = value; p->next = cacheHead; p->pre = cacheHead->pre; cacheHead->pre = p; cacheHead = p; mp[cacheHead->key] = cacheHead; } } else { Node * p = (Node *)malloc(sizeof(Node)); p->key = key; p->value = value; p->next = cacheHead; p->pre = NULL; cacheHead->pre = p; cacheHead = p; mp[cacheHead->key] = cacheHead; count++; } } else { Node *p = it->second; p->value = value; pushFront(p); } } } void pushFront(Node *cur) //双向链表删除节点,并将节点移动链表头部,O(1) { if(count==1) return; if(cur==cacheHead) return; if(cur==cacheTail) { cacheTail = cur->pre; } cur->pre->next = cur->next; //删除节点 if(cur->next!=NULL) cur->next->pre = cur->pre; cur->next = cacheHead; cur->pre = NULL; cacheHead->pre = cur; cacheHead = cur; } void printCache(){ Node *p = cacheHead; while(p!=NULL) { cout<<p->key<<" "; p=p->next; } cout<<endl; } }; int main(void) { LRUCache cache(3); cache.set(1,1); //cache.printCache(); cache.set(2,2); //cache.printCache(); cache.set(3,3); cache.printCache(); cache.set(4,4); cache.printCache(); cout<<cache.get(4)<<endl; cache.printCache(); cout<<cache.get(3)<<endl; cache.printCache(); cout<<cache.get(2)<<endl; cache.printCache(); cout<<cache.get(1)<<endl; cache.printCache(); cache.set(5,5); cache.printCache(); cout<<cache.get(1)<<endl; cout<<cache.get(2)<<endl; cout<<cache.get(3)<<endl; cout<<cache.get(4)<<endl; cout<<cache.get(5)<<endl; return 0; }
(2)用stl的list实现双向链表
#include <iostream> #include <map> #include <algorithm> #include <list> using namespace std; struct Node { int key; int value; }; class LRUCache{ private: int maxSize ; list<Node> cacheList; map<int, list<Node>::iterator > mp; public: LRUCache(int capacity) { maxSize = capacity; } int get(int key) { map<int, list<Node>::iterator >::iterator it = mp.find(key); if(it==mp.end()) //没有命中 { return -1; } else //在cache中命中了 { list<Node>::iterator listIt = mp[key]; Node newNode; newNode.key = key; newNode.value = listIt->value; cacheList.erase(listIt); //先删除命中的节点 cacheList.push_front(newNode); //将命中的节点放到链表头部 mp[key] = cacheList.begin(); } return cacheList.begin()->value; } void set(int key, int value) { map<int, list<Node>::iterator >::iterator it = mp.find(key); if(it==mp.end()) //没有命中 { if(cacheList.size()==maxSize) //cache满了 { mp.erase(cacheList.back().key); cacheList.pop_back(); } Node newNode; newNode.key = key; newNode.value = value; cacheList.push_front(newNode); mp[key] = cacheList.begin(); } else //命中 { list<Node>::iterator listIt = mp[key]; cacheList.erase(listIt); //先删除命中的节点 Node newNode; newNode.key = key; newNode.value = value; cacheList.push_front(newNode); //将命中的节点放到链表头部 mp[key] = cacheList.begin(); } } }; int main(void) { LRUCache cache(3); cache.set(1,1); cache.set(2,2); cache.set(3,3); cache.set(4,4); cout<<cache.get(4)<<endl; cout<<cache.get(3)<<endl; cout<<cache.get(2)<<endl; cout<<cache.get(1)<<endl; cache.set(5,5); cout<<cache.get(1)<<endl; cout<<cache.get(2)<<endl; cout<<cache.get(3)<<endl; cout<<cache.get(4)<<endl; cout<<cache.get(5)<<endl; return 0; }
标签:
原文地址:http://www.cnblogs.com/argenbarbie/p/5401133.html