LFU缓存, LeetCode#460,哈希表加平衡二叉树

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。

get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。

一个项目的使用次数就是该项目被插入后对其调用 get 和 put 函数的次数之和。使用次数会在对应项目被移除后置为 0。

进阶:
你是否可以在 O(1) 时间复杂度内执行两项操作?

示例:

LFUCache cache = new LFUCache( 2 /* capacity (缓存容量) */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 去除 key 2
cache.get(2); // 返回 -1 (未找到key 2)
cache.get(3); // 返回 3
cache.put(4, 4); // 去除 key 1
cache.get(1); // 返回 -1 (未找到 key 1)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

题解: 每次满了应当剔除最少使用次数的键, 当使用次数相等剔除, 最近最少使用的键将被去除。 所以需要键值按使用次数和最近使用时间来排序, 而且得实现插入和删除效率高, 使用平衡二叉树即可, 直接用set容器, 这是底层是红黑树, 每次删除最左下的即可, 即begin(), 而为了实现随机访问, 建立一个哈希表, 用于查找键是否存在,
当get操作时, 在哈希表中查找key, 若不存在返回-1, 存在就返回value并且使用次数加一, 最近使用时间更新, 同时将此更新到set中
当put操作时, 在哈希表中查找key, 若不存在, 新建哈希表键值对, 并将值插入set, 若存在, 则哈希表和set更新即可

想到LRU是用哈希表加链表实现删除最近最少访问的, 而LFU当操作次数相同时也是删除最近最少访问的, 故LRU也可用这种方式实现

struct Node {
	int cnt; //操作次数
	int time; //最近访问时间
	int key, value; //键值对
	Node(int c, int t, int k, int v) :cnt(c), time(t), key(k), value(v) {} //构造方法

	bool operator < (const Node& t) const { //set中排序方法
		return cnt == t.cnt ? time < t.time : cnt < t.cnt;
	}
};

class LFUCache {
	int capacity;
	int time;
	set<Node> s;
	map<int, Node> m;

public:
	LFUCache(int c) {
		capacity = c;
		time = 0;
		s.clear();
		m.clear();
	}

	int get(int key) {
		auto it = m.find(key);
		if (it == m.end()) return -1;

		Node cache = it->second;
		s.erase(cache);
		cache.time = time++;
		cache.cnt++;
		it->second = cache;
		s.insert(cache);
		return cache.value;
	}

	void put(int key, int value) {
		if (capacity == 0) return; //此处应特别判定
		auto it = m.find(key);
		if (it == m.end())
		{
			if (m.size() == capacity)
			{
				m.erase(s.begin()->key);
				s.erase(s.begin());
			}
			Node cache = Node(1, time++, key, value);
			m.insert(make_pair(key, cache));
			s.insert(cache);
		}
		else
		{
			auto it = m.find(key);
			Node cache = it->second;
			s.erase(cache);
			cache.cnt++;
			cache.time = time++;
			cache.value = value;
			it->second = cache;
			s.insert(cache);
		}
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值