福哥答案2021-01-23:
这道题复杂度太高,短时间内很难写出来。面试的时候不建议手撕代码。
一个存节点的map+一个存桶的map+一个存桶的双向链表。桶本身也是一个双向链表。
存节点的map:key是键,value是节点。
存桶的map:key是次数,value是桶。
代码用golang编写,代码如下:
package main
import (
"container/list"
"fmt"
)
func main() {
cache := Constructor(2)
cache.Put(1, 1)
cache.Put(2, 2)
cache.Get(1) // 返回 1
cache.Put(3, 3) // 去除键 2
cache.Get(2) // 返回 -1(未找到)
cache.Get(3) // 返回 3
cache.Put(4, 4) // 去除键 1
cache.Get(1) // 返回 -1(未找到)
cache.Get(3) // 返回 3
cache.Get(4) // 返回 4
}
type LFUCache struct {
Cap int
Len int
//map缓存,键存key,值存kv和前后
KeyCache map[int]*list.Element //key存键,value存节点
List *list.List
FreqCache map[int]*list.Element //key存次数,value存桶
}
func Constructor(capacity int) LFUCache {
ret := LFUCache{}
ret.Cap = capacity
ret.KeyCache = make(map[int]*list.Element) //元素存节点
ret.FreqCache = make(map[int]*list.Element) //元素存桶
ret.List = list.New()
return ret
}
func (this *LFUCache) Get(key int) int {
//已经找到当前元素了
v := this.KeyCache[key]
if v == nil {
fmt.Println(-1)
return -1
}
//移动
this.curNodeMoveToNextBucket(v)
//返回当前元素的值
fmt.Println(v.Value.([]int)[1])
return v.Value.([]int)[1]
}
//当前节点移动到下一个桶
func (this *LFUCache) curNodeMoveToNextBucket(v *list.Element) {
//根据当前节点的次数找到当前桶
curbucket := this.FreqCache[v.Value.([]int)[2]]
//找下一桶,找不到创建新桶
nextbucket := this.FreqCache[v.Value.([]int)[2]+1]
if nextbucket == nil {
nextbucket = this.List.InsertAfter(list.New(), curbucket)
this.FreqCache[v.Value.([]int)[2]+1] = nextbucket
}
//把当前节点放在下一桶里
//nextbucket.Value.(*list.List).PushBack(v.Value),这样的代码,leetcode不能通过。原因是元素移动后,已经不是以前的元素了。所以map需要重新赋值。这个错误,我花了1个小时才找到,请谨慎。
this.KeyCache[v.Value.([]int)[0]] = nextbucket.Value.(*list.List).PushBack(v.Value)
//当前桶删除当前节点
curbucket.Value.(*list.List).Remove(v)
//如果当前桶为空,直接删除当前桶。
if curbucket.Value.(*list.List).Len() == 0 {
this.List.Remove(curbucket)
delete(this.FreqCache, v.Value.([]int)[2])
}
//当前节点次数加1
v.Value.([]int)[2]++
}
func (this *LFUCache) Put(key int, value int) {
if this.Cap == 0 {
return
}
if v, ok := this.KeyCache[key]; ok { //缓存里有
//修改值
v.Value.([]int)[1] = value
//移动
this.curNodeMoveToNextBucket(v)
} else { //缓存里没有
if this.Len == this.Cap {
//获取可能需要删除的桶
deleteBucket := this.List.Front()
//获取需要删除的元素
deleteE := deleteBucket.Value.(*list.List).Front()
//删除元素
delete(this.KeyCache, deleteE.Value.([]int)[0])
deleteBucket.Value.(*list.List).Remove(deleteE)
//可能需要删除的桶如果没有元素,删除桶。并且需要删除的元素的次数不是1
if deleteBucket.Value.(*list.List).Len() == 0 {
this.List.Remove(deleteBucket)
delete(this.FreqCache, deleteE.Value.([]int)[2])
}
} else {
this.Len++
}
//获取次数为1的桶
oneTimeBucket := this.FreqCache[1]
//获取不到就创建桶
if oneTimeBucket == nil {
oneTimeBucket = this.List.PushFront(list.New())
this.FreqCache[1] = oneTimeBucket
}
this.KeyCache[key] = oneTimeBucket.Value.(*list.List).PushBack([]int{key, value, 1})
}
}
执行结果如下:
有疑问加站长微信联系(非本文作者)