golang实现LRU算法 go lru
Least Recently used
本文基于LeetCode146.LRU缓存实现
LRU的定义
如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。所以,当指定的空间已存满数据时,应当把最久没有被访问到的数据淘汰
数据结构:
- 哈希表:键为关键字key,值为*DLinkNode,映射对应链表中的节点地址,用于在O(1)的时间复杂度内找到对应的缓存。
- 双向链表:靠近头部的元素是最近使用的,靠近尾部则是最久未使用的。
//LRUCache定义
type LRUCache struct {
size int
capacity int
cache map[int]*DLinkNode
head, tail *DLinkNode
}
//双向链表节点
type DLinkNode struct {
key,value int
pre, next *DLinkNode
}
实现LRU需要两个动作:
- 获取数据Get(key int),如果关键字存在缓冲中,则获取关键字的值,否则返回-1.
- 写入数据Put(key, value int)如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
//缓存实现的功能
//如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
func (this *LRUCache) Get(key int)
//如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出最久未使用的关键字
func (this *LRUCache) Put(key int, value int)
了解了实现LRU的数据结构及方法后,需要关注具体的代码实现,如链表的更新方式等
//一些链表的操作
//初始化一个新节点
func initDLinkedNode(key, value int) *DLinkNode
//更新到链头,用于key命中的情况下,不改变缓存的size
func (this *LRUCache) UpdateToHead(node *DLinkNode)
//删除链尾元素
func (this *LRUCache) DeleteLast()
//添加新元素,用于key未命中时,size+1
func (this *LRUCache) InsertNewHead(node *DLinkNode)
LRU的代码实现
//哈希表
type LRUCache struct {
size int
capacity int
cache map[int]*DLinkNode
Head, Tail *DLinkNode
}
//缓存链表
type DLinkNode struct {
key,value int
Pre, Next *DLinkNode
}
//初始化一个新节点
func InitDlinkNode(key, value int) *DLinkNode {
return &DLinkNode{key,value,nil,nil}
}
//初始化哈希表和缓存链表
func Constructor(capacity int) LRUCache {
l := LRUCache{
0,
capacity,
map[int]*DLinkNode{},
InitDlinkNode(0, 0),
InitDlinkNode(0, 0),
}
l.Head.Next = l.Tail
l.Tail.Pre = l.Head
return l
}
func (this *LRUCache) Get(key int) int {
//缓存未命中,返回-1
if _,ok := this.cache[key];!ok {
return -1
}
//缓存命中,返回对应关键字的值,并置为最近访问
node := this.cache[key]
this.UpdateToHead(node)
return node.value
}
func (this *LRUCache) Put(key int, value int) {
//如果插入值不存在,插入新的key-value,维护缓存长度
if _,ok := this.cache[key];!ok {
node := InitDlinkNode(key, value)
for this.size >= this.capacity {
this.DeleteLast()
}
this.cache[key] = node
this.InsertNewHead(node)
}else {
//插入值已存在,将对应缓存置为最新访问
node := this.cache[key]
node.value = value
this.UpdateToHead(node)
}
}
//更新到链头,用于key命中的情况下,不改变缓存的size
func (this *LRUCache) UpdateToHead(node *DLinkNode) {
node.Pre.Next = node.Next
node.Next.Pre = node.Pre
temp := this.Head.Next
this.Head.Next = node
node.Pre = this.Head
node.Next = temp
temp.Pre = node
}
//删除链尾元素
func (this *LRUCache) DeleteLast() {
node := this.Tail.Pre
this.Tail.Pre = node.Pre
node.Pre.Next = node.Next
node.Pre = nil
node.Next = nil
this.size--
delete(this.cache, node.key)
}
//添加新元素,用于key未命中时,size+1
func (this *LRUCache) InsertNewHead(node *DLinkNode) {
temp := this.Head.Next
this.Head.Next = node
node.Pre = this.Head
temp.Pre = node
node.Next = temp
this.size++
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!