基于Golang实现LRU

LRU Least Recently Used的缩写

即最近最少使用,在页面置换和缓存过期key的淘汰中有用到

维持了一个map存储每个节点,另外维持了一个双端列表,来实现删除和插入操作

基本结构:

type LinkNode struct {
    key, val  int
    pre, next *LinkNode
}

type LRUCache struct {
    m          map[int]*LinkNode // 指向哈希表的指针
    cap        int               // 长度
    head, tail *LinkNode         // 两个哨兵
}

func Constructor(capacity int) LRUCache {
    //采用虚拟头尾节点,这两个节点始终处于首位,但不计入map中
    head := &LinkNode{-1, 0, nil, nil}
    tail := &LinkNode{-1, 0, nil, nil}
    head.next = tail
    tail.pre = head
    return LRUCache{make(map[int]*LinkNode), capacity, head, tail}
}

关键点:首尾节点是虚拟节点,无论怎么插入删除它们都存在,在cap中不会计算这两个节点,删除也是删除tail节点的前一个节点

Get方法:

func (this *LRUCache) Get(key int) int {
    m := this.m
    if node, ok := m[key]; ok {
        this.moveToHead(node)
        return node.val
    }
    return -1
}

func (this *LRUCache) moveToHead(node *LinkNode) {
    if node.pre != nil && node.next != nil{
        node.pre.next = node.next
        node.next.pre = node.pre
    }
    node.pre = this.head
    node.next = this.head.next
    this.head.next.pre = node
    this.head.next = node
}

Put方法:

func (this *LRUCache) Put(key int, value int) {
    m := this.m
    cap := this.cap
    tail := this.tail
    if node, ok := m[key]; ok {
        //插入新的值
        node.val = value
        this.moveToHead(node)
    } else {
        newNode := &LinkNode{key, value, nil, nil}
        if len(m) == cap {
            rmTail := tail.pre
            rmTail.pre.next = tail
            tail.pre =rmTail.pre
            rmTail.next = nil
            delete(m, rmTail.key)
        }
        this.moveToHead(newNode)
        m[key] = newNode
    }
}

关键点:由于双向链表的缘故,在插入或者删除时会一定注意检查前后节点的双向指针的指向问题

源码地址

leetcode地址

 

 

posted @ 2021-06-15 17:21  LeeJuly  阅读(166)  评论(0编辑  收藏  举报