Leetcode146:LRU缓存(高频题)

问题描述

 

Leetcode146:LRU缓存(高频题)_第1张图片

 思路简述

LRU缓存是非常常用的页面置换算法,在操作系统、Redis内存型数据库里面都大量用到了LRU的思想,核心思想是通过双向链表和哈希表来实现,每次淘汰就淘汰双向链表的末尾节点,插入或者查找元素时就将(新)节点移动到链表头,表示是最近刚使用过的元素,双向链表的末尾节点就是最近最少使用到的元素,也是在Capacity满了以后需要淘汰的。

具体的说,代码实现的时候需要自建双链表:

GET操作时,

        先判断是否存在,不存在就直接返回-1;

        如果存在,则通过哈希表找到这个Node,然后将Node移动到双向链表的表头;

PUT操作的时候,也是需要先判断key是否存在,

        如果存在,则不用new(Node),将对应节点移动到双向链表表头,并更新节点的Val值

        如果不存在,则需要判断当前链表是否已满:

                如果没有满就正常new(Node)然后插入链表头,并记录在哈希表;

                如果满了就需要淘汰元素,淘汰最后一个节点,并在哈希表中删除对应的Key值,然后插入新节点到链表头并在哈希表中更新。

代码实现

import "fmt"


type Node struct{
    Key int
    Val int
    Pre *Node
    Next *Node
}

type DoubleLinkList struct{
    Head *Node
    Tail *Node
}

type LRUCache struct {
    LinkList DoubleLinkList
    Cache map[int]*Node
    Capacity int
}

func Constructor(capacity int) LRUCache {
    list := new(DoubleLinkList)
    list.Head = new(Node)
    list.Head.Val = -99
    list.Tail = new(Node)
    list.Tail.Val = -99

    list.Head.Next = list.Tail
    list.Tail.Next = nil
    list.Tail.Pre = list.Head

    cache := new(LRUCache)
    cache.LinkList = *list
    cache.Cache = make(map[int]*Node)
    cache.Capacity = capacity

    return *cache
}


func (this *LRUCache) Get(key int) int {
    if node, ok := this.Cache[key]; ok {
        res := node.Val

        // 将node移动到双端链表的表头
        node.Pre.Next = node.Next
        node.Next.Pre = node.Pre
        node.Next = nil
        node.Pre = nil
        node.Next = this.LinkList.Head.Next
        node.Pre = this.LinkList.Head
        this.LinkList.Head.Next.Pre = node
        this.LinkList.Head.Next = node
        
        // fmt.Println("GET", key, res)
        // this.PrintLinkList()

        return res
    }else{
        return -1
    }
}


func (this *LRUCache) Put(key int, value int)  {
    if node, ok := this.Cache[key]; ok { // 存在 覆盖 同时移动到链表头
        // 覆盖
        // this.Cache[key].Val = value
        node.Val = value

        // 移动到链表头
        node.Pre.Next = node.Next
        node.Next.Pre = node.Pre
        node.Next = this.LinkList.Head.Next
        node.Pre = this.LinkList.Head
        this.LinkList.Head.Next.Pre = node
        this.LinkList.Head.Next = node
    }else{ // 不存在 将节点插入到双端链表的表头
        newNode := new(Node)
        newNode.Key = key
        newNode.Val = value

        // 判断满没有
        if len(this.Cache) == this.Capacity{// 满了就需要淘汰
            delete(this.Cache, this.LinkList.Tail.Pre.Key)

            // 哈希表中记录新插入的节点
            this.Cache[key] = newNode
             // 淘汰最后一个节点
            this.LinkList.Tail.Pre = this.LinkList.Tail.Pre.Pre
            this.LinkList.Tail.Pre.Next = this.LinkList.Tail

            // 插入新节点
            newNode.Next = this.LinkList.Head.Next
            newNode.Pre = this.LinkList.Head
            this.LinkList.Head.Next = newNode
            newNode.Next.Pre = newNode
            
        }else{ // 没有满直接插入 同时移动到表头
            this.Cache[key] = newNode
            
            newNode.Next = this.LinkList.Head.Next
            newNode.Pre = this.LinkList.Head
            this.LinkList.Head.Next = newNode
            newNode.Next.Pre = newNode
        }
    }
}

你可能感兴趣的:(数据结构,LRU,leetcode,双向链表,哈希表)