緩存算法LRU和LFU

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();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章