题目
- LFU 缓存
题目描述
请你为最不经常使用(LFU)缓存算法设计并实现数据结构。
实现 LFUCache 类:
LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。
void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。
当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除最久未使用的键。
思路
1、定义key到val的映射,为了能实现O(1)时间复杂度的访问
2、定义key到freq的映射,为了能实现O(1)时间复杂度定位key在哪一个freq链表中
3、定义freq到key的映射,为能能实现快速的删除freq最小,时间最找的key
4、定义minFreq不需要遍历即可知晓那个一freq链表最小
代码
package leetcode
import (
"container/list"
)
type Node struct {
key int
val int
freq int
}
type LFUCache struct {
// key 到val的映射
keyToVal map[int]*list.Element
// freq到key列表的映射
freqTokeys map[int]*list.List
minFreq int
cap int
}
// Constructor
// 初始化函数
func Constructor(capacity int) LFUCache {
return LFUCache{
keyToVal: map[int]*list.Element{},
freqTokeys: map[int]*list.List{},
minFreq: 0,
cap: capacity,
}
}
// Get
// 获取值
func (this *LFUCache) Get(key int) int {
// 不存在时则返回-1
ele, ok := this.keyToVal[key]
if !ok {
return -1
}
// 增加key对应的freq
return this.updateFreq(ele)
}
// updateFreq
// 访问key的同时需要增加对应freq的次数
func (this *LFUCache) updateFreq(ele *list.Element) int {
data := ele.Value.(*Node)
// 处理freq to key的映射关系
curList, ok := this.freqTokeys[data.freq]
if !ok {
return -1
}
curList.Remove(ele)
// TODO 确认是否需要更新
if curList.Len() == 0 {
if data.freq == this.minFreq {
this.minFreq++
}
}
// 更新key与freq的关系,当key对应的freq增加时候,则存储到对应的链表中
data.freq++
newList, ok := this.freqTokeys[data.freq]
// 如果不存在链表则创建
if !ok {
newList = list.New()
this.freqTokeys[data.freq] = newList
}
newEle := newList.PushBack(data)
// 更新元素的freq
this.keyToVal[data.key] = newEle
return data.val
}
// Put
// 存入新的值
func (this *LFUCache) Put(key int, value int) {
if this.cap <= 0 {
return
}
// 如果key已存在,修改对应的val即可
if ele, ok := this.keyToVal[key]; ok {
data := ele.Value.(*Node)
data.val = value
// 调整key的freq次数
this.updateFreq(ele)
return
}
// key不存在则需要插入
// 容量已满的话需要淘汰一个frqp最小的key
if this.cap == len(this.keyToVal) {
this.removeMinFreqKey()
}
// 创建新的节点
newNode := &Node{
key: key,
val: value,
freq: 1,
}
// 更新freq to key的链表
newList, ok := this.freqTokeys[newNode.freq]
if !ok {
// 不存在则创建一个链表
newList = list.New()
this.freqTokeys[newNode.freq] = newList
}
newEle := newList.PushBack(newNode)
this.keyToVal[key] = newEle
this.minFreq = newNode.freq
}
// removeMinFreqKey
// 淘汰一个freq最小的key
func (this *LFUCache) removeMinFreqKey() {
minList, ok := this.freqTokeys[this.minFreq]
if !ok {
return
}
delE := minList.Front()
minList.Remove(delE)
data := delE.Value.(*Node)
delete(this.keyToVal, data.key)
}
参考
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/lf…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
[算法小抄@付东来]