###### tags: `leetcode` # 146. LRU Cache ## OOP in python ref = https://www.w3schools.com/python/python_inheritance.asp ### parent class ```python= class Person: def __init__(self, fname, lname): self.firstname = fname self.lastname = lname ``` ### inherit but change nothing ```python= class Student(Person): pass ``` ### override ```python= class Student(Person): def __init__(self, fname, lname): ``` ### override but also pass parameters to parent's constructor ```python= class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname) # same as Person.__init__(fname, lname) self.graduationyear = 2019 ``` ## OrderedDict in pyhon :::info ``` result.popitem(last=False) ``` `OrderedDict.popitem()` returns the first or last key-value, after deleting it. Setting `last` to `False` signals you wanted to remove the first. ::: ```python= from collections import OrderedDict class LRUCache(OrderedDict): # inheritance def __init__(self, capacity: int): self.capacity = capacity def get(self, key: int) -> int: if key not in self: return-1 self.move_to_end(key) # update its usage status return self[key] def put(self, key: int, value: int) -> None: if key in self: self.move_to_end(key) # update its usage status self[key] = value # if key was in OrderedDict, it has been move_to_end # if key isn't in OrderedDict, it will be attached to the end if len(self) > self.capacity: self.popitem(last = False) # `last = False` signals deleting the first not the last # Your LRUCache object will be instantiated and called as such: # obj = LRUCache(capacity) # param_1 = obj.get(key) # obj.put(key,value) ``` ## Hashmap + DoubleLinkedList One advantage of ***double linked list*** is that the node can remove itself without other reference. If it is a single linked list, we can't remove a certain node given by a hash-map. We have to start from the head of the list to find the previous node of this node if it is a single linked list ```python= class DLinkedNode(): def __init__(self): self.key = 0 self.value = 0 self.prev = None self.next = None class LRUCache(): def _add_node(self, node): """ Always add the new node right after head. """ node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def _remove_node(self, node): """ Remove an existing node from the linked list. """ prev = node.prev new = node.next prev.next = new new.prev = prev def _move_to_head(self, node): """ Move certain node in between to the head. """ self._remove_node(node) self._add_node(node) def _pop_tail(self): """ Pop the current tail. """ res = self.tail.prev self._remove_node(res) return res def __init__(self, capacity): """ :type capacity: int """ self.cache = {} self.size = 0 self.capacity = capacity self.head, self.tail = DLinkedNode(), DLinkedNode() self.head.next = self.tail self.tail.prev = self.head def get(self, key): """ :type key: int :rtype: int """ node = self.cache.get(key, None) # Use `None`, it will return `None` when not found if not node: return -1 # move the accessed node to the head; self._move_to_head(node) return node.value def put(self, key, value): """ :type key: int :type value: int :rtype: void """ node = self.cache.get(key) # note that self.cache = {} if not node: newNode = DLinkedNode() newNode.key = key newNode.value = value self.cache[key] = newNode self._add_node(newNode) self.size += 1 if self.size > self.capacity: # pop the tail tail = self._pop_tail() del self.cache[tail.key] self.size -= 1 else: # update the value. node.value = value self._move_to_head(node) ```