2021-01-23:LFU手撸,说下时间复杂度和空间复杂度。
福哥答案2021-01-23:
这道题复杂度太高,短时间内很难写出来。面试的时候不建议手撕代码。
一个存节点的map+一个存桶的map+一个存桶的双向链表。桶本身也是一个双向链表。
存节点的map:key是键,value是节点。
存桶的map:key是次数,value是桶。
代码用golang编写,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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}) } } |
执行结果如下:
公众号:福大大架构师每日一题
分类:
福大大架构师每日一题
标签:
算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具