缓存算法LRU和LFU

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值