C++ 手写LFU(460. LFU 缓存)

概念:

LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。

题目要求:原题

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

        1.LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
        2.int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。
        3.void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。
        4.为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

说明:

1.官方题解并未考虑页面长时间留存在内存中但是不再被使用的情况,因此并不需要右移,对于做题来说完全足够;

2.这里我添加了默认构造函数,所以可以使用hash_map内部重载的[ ]运算符,否则是只能通过迭代器访问并修改hash_map中的Node的(就像官方题解一那样)

3.整体思路就是分为get() put() 两部分,利用set中红黑树的性质对node进行默认排序,利用hash_map存储<key,Node>设置一个时间戳,当freq即访问频率一样时就淘汰最早使用过的内存页,重点在于Node结构体中默认构造函数的必要性和重载<运算符的必要性

4.这道题不能只用一个map搞定,因为map的话只能使用map<int,Node>,那么就不能按照Node排序,而是按照key排序了

代码:

#include<iostream>
#include<set>
#include<unordered_map>
using namespace std;
constexpr int default_capacity = 50; // 默认缓存容量
struct Node {
	int freq, time, key, val;
	Node():freq(1), key(0), val(0), time(0) {}; // 这里注意,虽然代码中默认构造函数根本没用到,但是hash_map在重载[]运算符的时候要用到T(),也就是Node()
	Node(int _key, int _val, int _time) :freq(1), key(_key), val(_val), time(_time){};
	bool operator < (const Node& rhs) const { // 重载 < 运算符,设置成常函数
		return freq == rhs.freq ? time < rhs.time : freq < rhs.freq;
	}
};
class LFU {
private:
	unordered_map<int, Node>key_table;
	set<Node>freq_set; // 利用set红黑树的性质,通过重载<运算符对Node在freq_set内部进行排序
	int time; // 时间戳
	int capacity; // 缓存容量
public:
	LFU():capacity(default_capacity), time(0) {}
	LFU(int _capacity):capacity(_capacity), time(0) {}
	~LFU() {}
	void put(int key, int value) {
		if (key_table.find(key) != key_table.end()) {
			Node node = key_table[key];
			freq_set.erase(node);
			++node.freq; // 更新访问频率
			node.val = value; // 更新值
			node.time = ++time; // 更新时间戳
			key_table[key] = node; // 由于node是临时对象,所以更新完node后要重新插入set和map
			freq_set.insert(node);
		}
		else {
			if (key_table.size() == capacity) { // 缓存满了
				auto node = freq_set.begin(); // 头指针,指向最少访问或者最早添加的缓存
				key_table.erase(node->key);
				freq_set.erase(node);
			}
			Node node = Node(key, value, ++time);
			freq_set.insert(node);
			key_table[key] = node;
		}
	}

	int get(int key) {
		if (key_table.find(key) != key_table.end()) {
			Node node = key_table[key];
			freq_set.erase(node);
			++node.freq;
			node.time = ++time;
			freq_set.insert(node);
			key_table[key] = node;
			return node.val;
		}
		return -1;
	}

	void print() { // 打印检查
		for (auto it = freq_set.begin(); it != freq_set.end(); ++it) {
			cout << it->key << " " << it->time << " " << it->freq << endl;
		}
	}
};

int main() {
	LFU lfu(2);
	lfu.put(1,1);
	lfu.put(2, 1);
	lfu.print();
	lfu.get(1);
	lfu.print();
	lfu.put(3,1);
	lfu.print();
	lfu.put(3, 2);
	return -1;
}

做笔记使用,如有差错或者更好理解的解法,还请多多指正!溜了溜了~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想是优秀社畜

您的打赏是对我最大的鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值