菜鸟笔记
提升您的技术认知

面试题

实现一个lru cache 算法lru cache c 三种解法java实现lru算法及编码实现lru策略缓存lru算法常见缓存算法和lru的c 实现设计循环双端队列(deque)lru 缓存结构 (c 哈希双链表实现)lru缓存机制删除单链表中的指定节点linux 内核经典面试题拼多多社招面经:redis是重点,讲一讲redis的内存模型线程、进程、协程的区别c 经典面试题面试官:我们只想要这样的c 工程师linux c/c 学习路线链表操作汇总c 11的智能指针面试题浏览器中输入url后发生的事情常用的限流算法http协议和https协议面试题网络编程面试题目总结c 后台面试题目如何实现lru算法?如何寻找无序数组中的第k大元素?布隆过滤器 - 如何在100个亿url中快速判断某url是否存在?如何实现大整数相加?c 面试题及基本知识点总结c 给定出栈序列判定是否合法消息队列面试题要点redis缓存击穿,失效以及热点keyag真人官方网的解决方案网页在浏览器上的渲染过程几种限流算法lru算法例题c/c 常见面试知识点总结附面试真题----20210529更新引入mq消息队列的作用及其优缺点mysql面试篇社招三年后端面试题60道测开面试题,背完直接涨工资二叉树的层序遍历(两种方法实现)bitmap 海量数据处理字符串倒序输出的五种方法c语言 输入10个数,统计出并输出正数、负数和0的个数字节三面:如何设计一个高并发系统架构,网络 面试36问,ddos攻击原理c 线程池使用 c 11 编写可复用多线程任务池lrucache结合any容器类型实现任意类型缓存c 实现lfu算法

c 实现lfu算法-ag真人官方网

阅读 : 44

lfu (least frequently used),是一种缓存算法,它对每一个数据块都有一个访问次数的记录,也就是一个数据块的访问次数越大,在将来越可能访问。

1.每个节点(数据块)都有一个访问次数,新插入的节点访问次数为1
2.相同访问次数的依据时间排序,后插入的在前面
3.当需要淘汰数据时,会从尾部,也就是访问次数从小到大,开始淘汰

这里是使用c 实现的lfu缓存:主要利用了一个multiset,unorder_map分别来根据访问次数排序和保存数据。

这里是需要使用的节点结构

#include 
#include <unordered_map>
#include 
using namespace std;
template
struct cachekey
{
	key		key;
	int		count;
	cachekey(key k) :key(k), count(1) {}
	void change(int x) {
		count  = x;
	}
    int getcount() {
		return count;
	}
};
template
struct cachenode
{
	cachekey* cachekey;
	val val;
	cachenode(cachekey* ck, val v) :cachekey(ck), val(v) {}
	cachenode(key k, val v):val(v) {
		cachekey = new cachekey(k);
	}
};
template
struct cmp     //仿函数,提供cachekey的比较方式
{
	bool operator()(cachekey const* x, cachekey const* y) {
		return x->count < y->count;
	}
};

c lfu

/*
lfu (least frequently used)
根据数据的历史访问次数来决定淘汰数据,也就是当前时刻访问次数大的数据,
在下一时刻也可能被访问到。
1.每个节点(数据块)都有一个访问次数
2.相同访问次数的依据时间排序,后插入的在前面
3.当需要淘汰数据时,会从尾部,也就是访问次数从小到大,开始淘汰
*/
template
class lfu
{
public:
	lfu(size_t size) :cachesize(size){
	}
	~lfu() {
		auto ite = cachemap.begin();
		while (ite != cachemap.end()) {
			delete ite->second->cachekey;
			delete ite->second;
			  ite;
		}
		cachemap.clear();
		cacheset.clear();
	}
public:
	void		put(key key, val val) {
		this->check_cache_size();
		auto node = new cachenode(key, val);
		cachemap[key] = node;
		cacheset.insert(node->cachekey);
	}
	bool		get(key key, val* val) {
		if (cachemap.count(key)) {
			auto node = this->removefromset(key);
			if(val)
				*val = node->val;
			node->cachekey->change(1);
			cacheset.insert(node->cachekey);
			return true;
		}
		return false;
	}
	val			get(key key) {
		val v;
		this->get(key, &v);
		return v;
	}
	void        remove(key key) {
        if(cachemap.count(key)){
            auto node = cachemap[key];
            this->removefromset(node->key);
            delete node->key;
            delete node;
            cachemap.erase(key);
        }
	}
	bool		exist(key key) {
		if (cachemap.count(key))
			return true;
		return false;
	}
	void		print_test() {
		auto ite = cacheset.begin();
		while (ite != cacheset.end()) {
			cout << "count: " << (*ite)->count << " key: " << (*ite)->key << endl;
			  ite;
		}
		cout << "======================================" << endl;
	}
private:
	//将节点从set中删除,返回删除的node
	void     removefromset(cachekey* key) {	
		auto ite = cacheset.find(key);
        while(ite != cacheset.end()){
            if(key == cachemap[(*ite)->key]->key){
                cacheset.erase(ite);
                break;
            }
            auto itex = ite;
              ite;
            if(ite != cacheset.end() && (*ite)->getcount() != (*itex)->getcount()){
                break;
            }
        }
	}
	//检查缓存数量是否超出了大小限制
	void	check_cache_size() {	
		while (cachemap.size() >= cachesize) {
			auto ite = cacheset.begin();
			cachemap.erase((*ite)->key);
			cacheset.erase(ite);
		}
	}
private:
	multiset*,cmp>            cacheset;	//利用红黑树根据访问次数排序
	unordered_map*>     cachemap;	//缓存map
	size_t                                       cachesize;	//缓存空间大小
};

lfu-aging lfu的改进版

由于lfu的淘汰策略是淘汰访问次数最小的数据块,但是新插入的数据块的访问次数为1,这样就会产生缓存污染,使得新数据块被淘汰。所以在lfu算法之上,引入访问次数平均值概念,当平均值大于最大平均值限制时,将所有节点的访问次数减去最大平均值限制的一半或一个固定值。

c lfu-aging

/*
lfu-aging 对于lfu的改进版
lfu对于新数据不友好,因为新插入的数据访问次数count=1,放在尾部
如果需要淘汰就会将新数据淘汰。
所以lfu-aging加入了平均访问次数的概念
如果节点的平均访问次数大于某个固定值x时,则将所有节点的count值减去x/2
这样可解决“缓存污染”
*/
template
class lfuaging
{
public:
	lfuaging(size_t size,int maxaverage = 10) 
		:cachesize(size), countmaxaverage(maxaverage), countcuraverage(1), countall(0){
	}
	~lfuaging() {
		auto ite = cachemap.begin();
		while (ite != cachemap.end()) {
			delete ite->second->cachekey;
			delete ite->second;
			  ite;
		}
		cachemap.clear();
		cacheset.clear();
	}
public:
	void		put(key key, val val) {
		this->check_cache_size();
		auto node = new cachenode(key, val);
		cachemap[key] = node;
		cacheset.insert(node->cachekey);
        this->average(1);	
	}
	bool		get(key key, val* val) {
		if (cachemap.count(key)) {
			auto node = this->removefromset(key);
			if (val)
				* val = node->val;
			node->cachekey->change(1);
			cacheset.insert(node->cachekey);
			this->average(1);
			return true;
		}
		return false;
	}
	val			get(key key) {
		val v;
		this->get(key, &v);
		return v;
	}
	void        remove(key key) {
		auto node = this->removefromset(key);
		if (node) {
			cachemap.erase(key);
			this->average(-node->cachekey->getcount());
			delete node->cachekey;
			delete node;
		}
	}
	bool		exist(key key) {
		if (cachemap.count(key))
			return true;
		return false;
	}
	void		print_test() {
		auto ite = cacheset.begin();
		while (ite != cacheset.end()) {
			cout << "count: " << (*ite)->count << " key: " << (*ite)->key << endl;
			  ite;
		}
		cout << "======================================" << endl;
	}
private:
	cachenode* removefromset(key key) {
		if (cachemap.count(key)) {
			auto node = cachemap[key];
			auto ite = cacheset.begin();
			while (ite != cacheset.end()) {
				if ((*ite) == node->cachekey) {
					cacheset.erase(ite);
					break;
				}
				  ite;
			}
			return node;
		}
		return nullptr;
	}
	void	check_cache_size() {
		while (cachemap.size() >= cachesize) {
			auto ite = cacheset.begin();
			cachemap.erase((*ite)->key);
			cacheset.erase(ite);
		}
	}
	void		average(int change) {
		countall  = change;
		int av = countall / cachemap.size();
		if (av >= countmaxaverage) {
			auto ite = cacheset.begin();
			while (ite != cacheset.end()) {
				(*ite)->change(-(countmaxaverage / 2));
				  ite;
			}
			countall = countall - ((countmaxaverage / 2) * cachemap.size());
		}
	}
private:
	multiset*, cmp>            cacheset;
	unordered_map*>      cachemap;
	size_t                                        cachesize;
	int                                           countmaxaverage;
	int                                           countcuraverage;
	int                                           countall;
};

lfu与lru对比

lfu算法的命中率会更高一些,且能解决一些周期性的访问带来命中率下降的情况。

 

网站地图