Two days ago, I failed to implement the optimized LRU cache in coding interview due to panic and stopped by the interviewer. Yes, I suck at coding interviews. I have implemented LRU cache before, so I know how it works and of course know how to implement it. But if you never heard of it, I think this explanation is enough.
Brief definition
LRU Cache is a combination of hash map and queue. Hash map will store the elements by keys and values while the queue keeps track the least recently used keys. We will implement the queue using doubly linked list.
Rules
Track each key usage
Set max capacity that LRU cache will handle
If the size is over the defined capacity, remove the least recently used data, and store the new data
Accessed (get/set) the data means we use the data so mark it as the most recently used data
Node Structure
Node structure will store the key, value, and its previous & next nodes (doubly linked list).
LRU will store the capacity, size (optional), the stored data, and it will track the most and the least recently used using tail and head pointer. Size is optional since you can use len(v Type) method.
Pop tail will remove the tail (most recently used) node. We will not use this method directly, but it will be used later whenever user reset the tail value.
func(l*LRU)Set(key,valueint){// check if the key exists// if it exists, we need to remove it// then we append it to the queue// 4th rule (mark it as the most recently used)ifval,isOk:=l.data[key];isOk{// this is the reason why we need to use popTaill.pop(val)l.size--}// 3rd ruleifl.size>=l.capacity{n:=l.popHead()delete(l.data,n.Key)l.size--}// push new datan:=NewNode(key,value)l.data[key]=nl.pushTail(n)l.size++}
Get
Get method will return the stored value depends on the given key. Remember the 4th rule.
Accessed (get/set) the data means we use the data so mark it as the most recently used data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func(l*LRU)Get(keyint)int{val,isOk:=l.data[key]if!isOk{return-1}// remove itl.pop(val)// then mark it as the most recently usedl.pushTail(val)returnval.Value}
Lastly, to ensure our queue rotation is correct, let’s implement the showQueue method.
packagemainimport"fmt"typeNodestruct{KeyintValueintPrev*NodeNext*Node}funcNewNode(key,valueint)*Node{return&Node{Key:key,Value:value,}}typeLRUstruct{capacityintsizeintdatamap[int]*Nodetail*Nodehead*Node}funcNewLRU(capacityint)*LRU{return&LRU{capacity:capacity,size:0,data:make(map[int]*Node),}}func(l*LRU)pushTail(n*Node){ifl.head==nil{l.head=nl.tail=nreturn}l.tail.Next=nn.Prev=l.taill.tail=nl.tail.Next=nil}func(l*LRU)popHead()*Node{ret:=l.headifl.head==l.tail{l.head=nil}else{l.head=l.head.Nextl.head.Prev=nil}returnret}func(l*LRU)popTail()*Node{ret:=l.tailifl.head==l.tail{l.head=nil}else{l.tail=l.tail.Prevl.tail.Next=nil}returnret}func(l*LRU)pop(n*Node)*Node{switchn{casel.head:returnl.popHead()casel.tail:returnl.popTail()}n.Next.Prev=n.Prevn.Prev.Next=n.Nextreturnn}func(l*LRU)Set(key,valueint){// check if the key exists// if it exists, we need to remove it// then we append it to the queue// 4th rule (mark it as the most recently used)ifval,isOk:=l.data[key];isOk{// this is the reason why we need to use popTaill.pop(val)l.size--}// 3rd ruleifl.size>=l.capacity{n:=l.popHead()delete(l.data,n.Key)l.size--}// push new datan:=NewNode(key,value)l.data[key]=nl.pushTail(n)l.size++}func(l*LRU)Get(keyint)int{val,isOk:=l.data[key]if!isOk{return-1}// remove itl.pop(val)// then mark it as the most recently usedl.pushTail(val)returnval.Value}func(l*LRU)ShowQueue(){fmt.Printf("Least ")forn:=l.head;n!=l.tail;n=n.Next{fmt.Printf("%v -> ",n.Key)}fmt.Println(l.tail.Key,"Most")}funcmain(){lru:=NewLRU(3)lru.Set(1,1)lru.Set(2,2)lru.Set(3,3)// Least 1 -> 2 -> 3 Mostlru.ShowQueue()// 2fmt.Println(lru.Get(2))// Least 1 -> 3 -> 2 Mostlru.ShowQueue()lru.Set(1,100)// Least 3 -> 2 -> 1 Mostlru.ShowQueue()lru.Set(4,4)// Least 2 -> 1 -> 4 Mostlru.ShowQueue()}
Thank you for reading!
···
Love This Content?
Any kind of supports is greatly appreciated! Kindly support me via Bitcoin, Ko-fi, Trakteer, or just continue to read another content. You can write a response via Webmention and let me know the URL via Telegraph.