LRU cache:鏈表+hash表
思路:維護一個鏈表,鏈表頭爲最近最少使用的,鏈表尾爲最近使用最多的,每次如果命中(如果不知道命中建議先看下操作系統)那麼把節點從鏈表刪去,再放到鏈表末尾,如果沒有命中,則把鏈表頭刪除,把新的放到末尾。至於查找是否命中,可以通過unordered_map來保存節點地址,這樣就可以O(1)找到了。代碼如下:
#include <cstdio>
#include <ctime>
#include <cstring>
#include <random>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define MAXN 105
#define MAXM 1000005
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 0x3f3f3f3f;
struct Node{
int data;
Node *pre;
Node *next;
Node(int d,Node* p) :data(d),pre(p),next(NULL){}
};
struct LRU{
Node *head;
Node *tail;
int size;
int LRUsize;
unordered_map<int, Node*>m;
LRU(int s) :size(0),LRUsize(s){
head = new Node(0, NULL);
tail = head;
}
void add(int data){
tail->next = new Node(data, tail);
tail = tail->next;
size++;
}
void del(Node *node){
size--;
node->pre->next = node->next;
if(node->next)
node->next->pre = node->pre;
delete node;
}
void solve(int data){
Node *now = (m.find(data) != m.end() ? m[data] : NULL);
if (now)
del(now);
else if (size == LRUsize){
m.erase(head->next->data);
del(head->next);
}
add(data);
m[data] = tail;
}
};
int main(){
LRU lru(4);
int a[20] = { 1, 2, 4, 5, 7, 2, 1, 7, 7, 4, 1, 4, 4, 1, 7 };
for (int i = 0; i < 15; ++i)
lru.solve(a[i]);
}
此代碼寫得比較爛,比較隨意暫時也沒空修改了。下面給LFU的思路和代碼,因爲項目需要用到LFU緩存所以這份代碼寫得比較嚴謹可讀性也比較高,測試過應該沒有問題。
思路:典型的雙重鏈表+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();