问题描述
思路简述
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
}
}
}