TARGET DECK: Leetcode FILE TAGS: Medium


Intuition

  • Make sure that you create two dummy nodes instead of one.
  • Pay close attention to detaching the node and then adding it to the most recently used node on every put and get (since that means that key will have been used).
  • Reuse those code portions as needed.

Cards

START Basic Front: LRU Cache Back: Helper functions used for doubly Linked List approach: detach(), evict(), insert(), update()

END

Complexity

Runtime

  • Average time complexity due to HashMap

Space

to store a HashMap as well as a Linked List

Code

More Modular

class DoubleLLNode:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.next = None
        self.prev = None
 
class LRUCache:
    def __init__(self, capacity: int):
        self.hm = {}
        self.capacity = capacity
        self.lru = DoubleLLNode(-1, -1)
        self.mru = DoubleLLNode(-1, -1)
        self.lru.next = self.mru
        self.mru.prev = self.lru  
 
    def detach(self, node):
        prev = node.prev
        nxt = node.next
 
        node.prev = None
        node.next = None
 
        prev.next = nxt
        nxt.prev = prev
        return node
 
    def evict(self, node):
        self.detach(node)
        del self.hm[node.key]
 
    # Inserts to MRU
    def insert(self, node):
        prev = self.mru.prev
        prev.next = node
        self.mru.prev = node
        node.prev = prev
        node.next = self.mru
    
    # Update MRU
    def update(self, node):
        self.insert(self.detach(node)) # Update MRU
 
    def get(self, key: int) -> int:
        print(key)
        if key in self.hm:
            val = self.hm[key].val
            self.update(self.hm[key])
            return val
        return -1
 
    def put(self, key: int, value: int) -> None:
        if key in self.hm:
            self.hm[key].val = value
            self.update(self.hm[key])
        else:
            if len(self.hm) == self.capacity:
                self.evict(self.lru.next)
            self.hm[key] = DoubleLLNode(key, value)
            self.insert(self.hm[key])
 
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

Original

class Node:
    def __init__(self, key=0, val=0):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None
 
class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cur_size = 0
        self.hm = {}
        self.lru = Node(-1, -1)
        self.mru = Node(-1, -1)
        self.lru.next = self.mru
        self.mru.prev = self.lru
 
    def detatch_node(self, node):
        prev = node.prev
        next = node.next
        next.prev = prev
        prev.next = next
        node.prev = None
        node.next = None
    
    def add_to_mru(self, node):
        prev = self.mru.prev
        prev.next = node
        self.mru.prev = node
        node.prev = prev
        node.next = self.mru
 
    def get(self, key: int) -> int:
        if key not in self.hm:
            return -1
        node = self.hm[key]
        self.detatch_node(node)
        self.add_to_mru(node)
        return node.val
 
    def put(self, key: int, value: int) -> None:
        if key in self.hm:
            node = self.hm[key]
            self.detatch_node(node)
            self.add_to_mru(node)
            self.hm[key].val = value
        else:
            if self.cur_size == self.capacity:
                node = self.lru.next
                self.detatch_node(node)
                del self.hm[node.key]
                self.cur_size -= 1
 
            node = Node(key, value)
            self.hm[key] = node
            self.add_to_mru(node)
            self.cur_size += 1
 
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

Notes


References