力扣連結:146. LRU 快取機制
思路:雜湊表 + 雙向連結串列
為什麼必須要用雙向連結串列?
因為我們需要刪除操作。刪除一個節點不光要得到該節點本身的指標,也需要操作其前驅節點的指標,而雙向連結串列才能支援直接查詢前驅,保證操作的時間複雜度 O(1)。
為什麼要在連結串列中同時儲存 key 和 val,而不是隻儲存 val?
當快取容量已滿,我們不僅僅要刪除最後一個節點,還要把雜湊表 中對映到該節點的 key 同時刪除,而這個 key 只能由 節點得到。如果 節點結構中只儲存 val,那麼我們就無法得知 key 是什麼,就無法刪除 雜湊表中的鍵,造成錯誤。
程式碼
我這裡是向尾部新增資料,所以頭部的是不活躍的資料值
type LRUCache struct { //LRU 快取結構
capacity int // 容量
m map[int]*Node //雜湊表
cache *NodeList //雙向連結串列
}
type Node struct{ //節點結構
key int
value int
prev *Node //前一個節點
next *Node //後一個節點
}
func initNode(key,value int)*Node{ //初始化節點
return &Node{
key:key,
value:value,
}
}
type NodeList struct{ //連結串列結構
head *Node //連結串列頭節點
last *Node //連結串列尾節點
size int //元素個數
}
func initList ()*NodeList{ //初始化連結串列
dil:=&NodeList{
head:initNode(0,0),
last:initNode(0,0),
size:0,
}
dil.head.next=dil.last
dil.last.prev=dil.head
return dil
}
func (this *NodeList)addNodeinlist(node *Node){ //向連結串列中插入節點,向連結串列尾部插節點
node.prev=this.last.prev
this.last.prev=node
node.prev.next=node
node.next=this.last
this.size++
}
func (this *NodeList)deleteNodeinlist (node *Node){ //刪除連結串列中的某一結點
node.prev.next=node.next
node.next.prev=node.prev
node.next=nil
node.prev=nil
this.size--
}
func (this *NodeList)delfirstNode()*Node{ //刪除第一個節點,並且返回
if this.head.next==this.last{
return nil
}
t:=this.head.next
this.deleteNodeinlist(t)
return t
}
func Constructor(capacity int) LRUCache { //初始化 LRU 快取
return LRUCache{
capacity:capacity,
m:make(map[int]*Node,0),
cache:initList(),
}
}
func (this *LRUCache)addkey(key,value int){ //新增元素
node:=initNode(key,value)
//增加map對映
this.m[key]=node
//在連結串列中新增元素
this.cache.addNodeinlist(node)
}
func (this *LRUCache)makekey(key int){ // 將某個 key 調整為最近使用的元素
//找到節點
node:=this.m[key]
//刪除節點
this.cache.deleteNodeinlist(node)
// 新增到連結串列尾部
this.cache.addNodeinlist(node)
}
func (this *LRUCache)deletekey(key int){ //刪除元素
//刪除連結串列中節點
this.cache.deleteNodeinlist(this.m[key])
//刪除map對映
delete(this.m,key)
}
func (this *LRUCache)deletefirkey(){ //刪除最久未使用的元素
// 連結串列的第一個就是最近最少使用的元素
node:=this.cache.delfirstNode()
// 刪除對映
delete(this.m,node.key)
}
func (this *LRUCache) Get(key int) int {
if _,ok:=this.m[key];ok{
//存在
this.makekey(key) //將某個 key 調整為最近使用的元素
return this.m[key].value
}else{
//不存在
return -1
}
}
func (this *LRUCache) Put(key int, value int) {
// 檢查key存不存在
if _,ok:=this.m[key];ok{
//存在
//刪除元素
this.deletekey(key)
//新增元素到尾部
this.addkey(key,value)
}else{
//不存在
if this.capacity==this.cache.size{
//快取達到上限
//刪除最久未使用的元素
this.deletefirkey()
}
//新增元素到尾部
this.addkey(key,value)
}
}
參考:
https://leetcode-cn.com/problems/lru-cache/solution/jian-dan-shi-li-xiang-xi-jiang-jie-lru-s-exsd/