How is the LRU deleted in this LRUCache implementation?

Question:

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]?

Asked By: PineNuts0

||

Answers:

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:

  1. if key present -> delete the key from dictionary and node
    correspoinding to it from LruCache and add that node at tail
    part(second last)
  2. if key not present -> add the key and node created at last(tail part). check if size of dict exceed the self.capacity or
    not. if yes then remove the first element (After head) from lrucache
    double linked list.

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

Answered By: sahasrara62
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.