LRU cache:链表+hash表
思路:维护一个链表,链表头为最近最少使用的,链表尾为最近使用最多的,每次如果命中(如果不知道命中建议先看下操作系统)那么把节点从链表删去,再放到链表末尾,如果没有命中,则把链表头删除,把新的放到末尾。至于查找是否命中,可以通过unordered_map来保存节点地址,这样就可以O(1)找到了。代码如下:
#include <unordered_map>
#include <list>
class LRUCache {
private:
struct Node{
int key, value;
};
int capacity_;
unordered_map<int, list<Node>::iterator>map_;
list<Node> l_;
public:
LRUCache(int capacity):capacity_(capacity) {
}
int get(int key) {
auto iter_node = map_.find(key);
if(iter_node == map_.end())
return -1;
l_.push_front(*iter_node->second);
l_.erase(iter_node->second);
map_[key] = l_.begin();
return iter_node->second->value;
}
void put(int key, int value) {
if(capacity_ <= 0) return;
//先检查在不在里面
auto iter_node = map_.find(key);
if(iter_node == map_.end()){
//检查容量
if(l_.size() >= capacity_){
//删除最后一个
auto last = --l_.end();
map_.erase(last->key);
l_.erase(last);
}
}
else{
l_.erase(iter_node->second);
map_.erase(key);
}
Node node{key, value};
l_.push_front(node);
map_[key] = l_.begin();
}
};
下面是LFU代码,还没重构成直接用STL的版本,先凑合着。
思路:典型的双重链表+hash表实现,首先是一个大链表,链表的每个节点都是一个小链表附带一个值表示频度,小链表存的是同一频度下的key value节点。hash表存key到大链表节点的映射(key,freq_node)和key到小链表节点的映射(key,key_node).
首先是get方法:用于判定是否命中,命中则通过引用参数返回value,如命中返回true,否则false。
首先所有的节点都可以通过unordered_map来查找到,所以直接通过key来判定map里是否存在,如果存在则命中,否则没有命中。
if 命中,则更新节点频度。
set方法:if(cache容量==0) 返回,如果容量满了,则删除频度最小而且最久未使用的节点。增加该节点到频度为1的头位置(在代码里是认为该节点原来放在head,更新该节点的频度,跟上面的一样操作)。
更新节点频度:
if 不存在下一个频度的链表,则增加一个。
把当前节点放到下一个频度的链表的头位置.
大致如上所述,下面给完整代码:
#include "LFUCache.h"
void KeyList::init(int fq)
{
freq=fq;
head=last=new Node<Key>;
head->setNext(NULL);
}
void KeyList::destory(){
while(head){
key_node pre=head;
head=head->getNext();
delete pre;
}
}
int KeyList::getFreq(){
return freq;
}
void KeyList::add(key_node &node){
if(head->getNext()){
head->getNext()->setPre(node);
//printf("is not one\n");
}
else
last=node;
node->setNext(head->getNext());
node->setPre(head);
head->setNext(node);
//printf("last key=%d\n",last->getValue().key);
}
void KeyList::del(key_node &node){
node->getPre()->setNext(node->getNext());
if(node->getNext())
node->getNext()->setPre(node->getPre());
else
last=node->getPre();
}
bool KeyList::isEmpty(){
return head==last;
}
key_node KeyList::getLast(){
return last;
}
LFUCache::LFUCache(int c)
: capacity(c)
{
head=new Node<KeyList>;
head->getValue().init(0);
head->setNext(NULL);
}
LFUCache::~LFUCache(){
while(head){
freq_node pre=head;
head=head->getNext();
pre->getValue().destory();
delete pre;
}
}
void LFUCache::addfreq(key_node &nowk,freq_node &nowf){
freq_node nxt;
if(!nowf->getNext()||nowf->getNext()->getValue().getFreq()!=nowf->getValue().getFreq()+1){
//插入freqnode
//printf("new freqnode!\n");
nxt=new Node<KeyList>;
nxt->getValue().init(nowf->getValue().getFreq()+1);
if(nowf->getNext())
nowf->getNext()->setPre(nxt);
nxt->setNext(nowf->getNext());
nowf->setNext(nxt);
nxt->setPre(nowf);
}
else
nxt=nowf->getNext();
fmap[nowk->getValue().key]=nxt;
//移动keynode
if(nowf!=head){
nowf->getValue().del(nowk);
//printf("nowf is not head!\n");
}
nxt->getValue().add(nowk);
if(nowf!=head&&nowf->getValue().isEmpty())
del(nowf);
}
bool LFUCache::get(int &key,int &v){
if(fmap.find(key)!=fmap.end()){
//命中
key_node nowk=kmap[key];
freq_node nowf=fmap[key];
v=nowk->getValue().value;
addfreq(nowk,nowf);
return true;
}
return false;
}
void LFUCache::set(int &key,int &v){
if(!capacity)
return;
//printf("kmapsize=%d capacity=%d\n",kmap.size(),capacity);
if(kmap.size()==capacity){
freq_node headnxt=head->getNext();
key_node last=headnxt->getValue().getLast();
headnxt->getValue().del(last);
//printf("key=%d\n",last->getValue().key);
kmap.erase(last->getValue().key);
fmap.erase(last->getValue().key);
delete last;
if(headnxt->getValue().isEmpty())
del(headnxt);
}
key_node nowk=new Node<Key>;
nowk->getValue().key=key;
nowk->getValue().value=v;
addfreq(nowk,head);
kmap[key]=nowk;
}
void LFUCache::del(freq_node &node){
node->getPre()->setNext(node->getNext());
if(node->getNext())
node->getNext()->setPre(node->getPre());
node->getValue().destory();
delete node;
}
#pragma once
#include "../MemoryPool/MemoryPool.h"
#include <unordered_map>
#include <string>
#include "../conf/Conf.h"
#include "../Mutex/MutexLock.h"
using std::string;
template<typename T>
class Node{
private:
T value;
Node* pre;
Node* next;
public:
void setPre(Node *p){
pre=p;
}
void setNext(Node *n){
next=n;
}
Node* getPre(){
return pre;
}
Node* getNext(){
return next;
}
T& getValue(){
return value;
}
};
struct Key{
string key,value;
};
typedef Node<Key>* key_node;
class KeyList{
private:
int freq;
key_node head;
key_node last;
public:
void init(int fq);
void destory();
int getFreq();
void add(key_node &node);
void del(key_node &node);
bool isEmpty();
key_node getLast();
};
typedef Node<KeyList>* freq_node;
class LFUCache{
private:
freq_node head;
int capacity;
MutexLock mutex;
std::unordered_map<string,key_node> kmap;//key到keynode的映射
std::unordered_map<string,freq_node> fmap;//key到freqnode的映射
void addfreq(key_node &nowk,freq_node &nowf);
void del(freq_node &node);
public:
void init();
~LFUCache();
bool get(string &key,string &value);//通过key返回value并进行LFU操作
void set(string &key,string &value);//更新LFU缓存
};
LFUCache& getCache();