I'm going over the optimal Leetcode solution for LRU Cache LeetCode Medium question. The original problem can be found here: https://leetcode.com/problems/lru-cache/description/
I'm confused by a specific portion of the solution code.
class Node:
def __init__(self, key, val):
self.key, self.val = key, val
self.prev = self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.cache = {} # map key to node
self.left, self.right = Node(0, 0), Node(0, 0)
self.left.next, self.right.prev = self.right, self.left
# remove node from list
def remove(self, node):
prev, nxt = node.prev, node.next
prev.next, nxt.prev = nxt, prev
# insert node at right
def insert(self, node):
prev, nxt = self.right.prev, self.right
prev.next = nxt.prev = node
node.next, node.prev = nxt, prev
def get(self, key: int) -> int:
if key in self.cache:
self.remove(self.cache[key])
self.insert(self.cache[key])
return self.cache[key].val
return -1
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.remove(self.cache[key])
self.cache[key] = Node(key, value)
self.insert(self.cache[key])
if len(self.cache) > self.cap:
# remove from the list and delete the LRU from hashmap
**lru = self.left.next
self.remove(lru)**
del self.cache[lru.key]
Why am I deleting the lru? In other words why is self.left.next represent the least recently used key?
To illustrate this let's say your capacity param is 2. And you currently have two nodes comprising your doubly linked list: [1,1] and [2,2]
Now you put a 3rd node [3,3] so the linked list looks like this: [2,2] [1,1] [3,3]. But you have exceeded the capacity constraint of two so you need to remove the LRU (least recently used) node which is [2,2]. For lru = self.left.next ... why does it equal [2,2] and not [1,1]?
code is simple, it's just a double linked list with self.head and self.tail (two pointer)define initially , and node info is stored in dictionary
Node(0, 0)) <->(DO node insertion delete here) <-> (Node(0,0)
^ ^
head tail
all deletion and updation happenining in above space between these two node.
in simple terms:
for addition
-> element is added at tail (as second last element)
for deletion
-> element after head is deleted.
addition and deletion is same as what happen at double linked list.No need to explain.
accessing of node is done via dictionary by mapping key to node.
work flow:
when you read a node having a key:
in your example [[2,2], [1,1]]
here is double linked list in diagram
start ->
head (node(0, 0)) <-> tail (node(0,0)) # lru cache created
[2,2] added put operation
head (node(0, 0)) <-> Node(2,2) <-> tail (node(0,0))
[1,1] added put operation
head (node(0, 0)) <-> Node(2,2) <-> Node(1, 1) <-> tail (node(0,0))
[3,3] added put operation
head (node(0, 0)) <-> Node(2,2) <-> Node(1, 1) <-> Node(3,3) <->tail <->(node(0,0))
here size increase. since Node(2,2) used once and no get operation happen on it , it will be removed and cache become
head (node(0, 0)) <-> Node(1, 1) <-> Node(3,3) <->tail(node(0,0))
now suppose [1,3] put operation, having key present
happen then this is how list will be changed
head (node(0, 0)) <-> Node(3,3) <-> Node(1,3) <->tail (node(0,0))
you can see for each operation if element is accessed ie get
used and is found then element is added at right and remove from his present location
in case of put
it is added at right, if capacity increases then first element after head is removed.
Hope this help