题目
设计并实现最不经常使用(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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
show me the code-golang
题目难度是困难,但是其实考察的是数据结构的基本功。本题使用双哈希法,具体详见leetcode官方题解二。
时间复杂度:O(1),只用到了哈希表和链表的增删操作
空间复杂度:O(n),n为缓存长度
type LFUCache struct {
cache map[int]*Node
freq map[int]*DoubleList
ncap,size,minFreq int
}
func Constructor(capacity int) LFUCache {
return LFUCache{
cache:make(map[int]*Node),
freq:make(map[int]*DoubleList),
ncap:capacity,
}
}
func (this *LFUCache) Get(key int) int {
if node,ok := this.cache[key];ok {
this.IncFreq(node)
return node.val
}
return -1
}
func (this *LFUCache) Put(key int, value int) {
if this.ncap==0 {
return
}
if node,ok := this.cache[key];ok {
node.val = value
this.IncFreq(node)
} else {
if this.size>=this.ncap {
node := this.freq[this.minFreq].RemoveLast()
delete(this.cache,node.key)
this.size--
}
x := &Node{key:key,val:value,freq:1}
this.cache[key] = x
if this.freq[1] == nil {
this.freq[1] = CreateDL()
}
this.freq[1].AddFirst(x)
this.minFreq = 1
this.size++
}
}
//结点的频次增加
func (this *LFUCache) IncFreq(node *Node) {
_freq := node.freq
this.freq[_freq].Remove(node)
if this.minFreq==_freq&&this.freq[_freq].IsEmpty() {
this.minFreq++
delete(this.freq,_freq)
}
node.freq++
if this.freq[node.freq]==nil {
this.freq[node.freq]=CreateDL()
}
this.freq[node.freq].AddFirst(node)
}
type DoubleList struct {
head,tail *Node
}
type Node struct {
prev,next *Node
key,val,freq int
}
//新建双向链表
func CreateDL() *DoubleList {
head,tail := &Node{},&Node{}
head.next,tail.prev = tail,head
return &DoubleList {
head:head,
tail:tail,
}
}
func (this *DoubleList) AddFirst(node *Node) {
node.next = this.head.next
node.prev = this.head
this.head.next.prev = node
this.head.next = node
}
func (this *DoubleList) Remove (node *Node) {
node.prev.next = node.next
node.next.prev = node.prev
node.next = nil
node.prev = nil
}
func (this *DoubleList) RemoveLast() *Node {
if this.IsEmpty() {
return nil
}
last := this.tail.prev
this.Remove(last)
return last
}
func (this *DoubleList) IsEmpty() bool {
return this.head.next==this.tail
}
总结
基本功要扎实。