题目:
设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。
get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。
进阶:
你是否可以在 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lfu-cache
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
用了当初操作系统课程设计的大概思路实现的LFU,数组存值的方式,不是最优解
- Get操作: 直接访问存储的map,map中存在对应key的CacheItem则表示缓存命中,则更新访问数据,返回结果
- Put操作
- 判断缓存中是否已存在对应的key,存在则进行访问记录数据、缓存值更新
- 新增缓存中未存在的值,则先判断当前缓存容量是否已经满了,如果当前缓存容量满了,则要先通过LFU进行数据淘汰操作。
- 插入新的缓存值
淘汰数据方法思路
- 遍历先存在的所有缓存
- 通过对比每个缓存项的访问时间,以及最后一次访问时间决定淘汰数据的键值
(1)找出访问次数最少的CacheItem、Cache Key
(2)如果访问次数最少的缓存键存在多个,则通过每个缓存CacheItem最后访问时间判断哪个是最久没进行访问过的,也就是最后一次访问时间是最早最小的,决定最后淘汰缓存key - 将遍历筛选出来需要淘汰的缓存key,从缓存map中进行delete操作,即删除淘汰缓存
说明下需要更新访问信息的两种情况
- Get操作,命中缓存 ,需要更新缓存信息
- Put操作,Put进来的Key对应缓存已存在,则需要进行缓存值以及访问信息的数据更新。
优化大概思路
通过哈希表以及双向链表维护一个插入时间有序的数据结构,因为双向链表中删除、插入一个节点的时间复杂度都是O(1),在进行数据淘汰的时候,可直接删除有序链表中的head或tail Node实现数据淘汰,降低Put操作时间复杂度。
Go解题代码:
type CacheItem struct {
Value int //保存缓存项的值
VisitTimes int //该缓存访问次数
VisitTime int //最后一次访问时间