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
}

image

实现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++
}

posted @   peterrrr  阅读(969)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示