Original Problem

Past Solutions

Idea


  • The idea here is to use a Hash Map to keep a mapping between the key and the value. And we use a Double Linked List to keep track the least recently used key-value pair and most recently used key-value pair in constant time
  • The value in the hash map is mapped to a node inside the double linked list, so we are able to locate the least recently used key-value pair in linear time
  • We also make use of Virtual Node to make the linked list operations easy to manage. Refer to Linked List Tips to help with the solution implementation

Space & Time Analysis


The analysis method we are using is Algorithm Complexity Analysis

Space - O(n)

  • Ignore input size & language dependent space
  • we are using a Hash Map to hold the key-node pairs

Time - O(1)

  • Both get() and put() are O(1)

Codes


4th Attempt (Java)

public class Node {
    int key;
    int val;
    Node prev;
    Node next;
    Node(){}
 
    Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}
 
class LRUCache {
    int capacity;
    HashMap<Integer, Node> map;
    Node head;
    Node tail;
 
    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
 
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        
        // Recently Used
        Node currNode = map.get(key);
        remove(currNode);
        accessed(currNode);
 
        return currNode.val;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node currNode = map.get(key);
            currNode.val = value;
            
            // Recently Used (updated)
            remove(currNode);
            accessed(currNode);
            return;
        }
 
        if (map.size() < capacity) {
            Node newNode = new Node(key, value);
 
            // Recently Used (added)
            accessed(newNode);
            map.put(key, newNode);
            return;
        }
 
        // Evict
        Node lastNode = tail.prev;
        remove(lastNode);
        map.remove(lastNode.key);
        
        // Add
        Node newNode = new Node(key, value);
        map.put(key, newNode);
        // Recently Used (added)
        accessed(newNode);
    }
 
    void remove(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    void accessed(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next = node;
        node.next.prev = node;
    }
}
 
/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

Personal Reflection


  • Why it takes so long to solve: Rusty with Double Linked List implementation in Java
  • What you could have done better: Sketch out the things to note. For example, the place to update the Hash Map and the double linked list
  • What you missed: NIL
  • Ideas you’ve seen before: hashmap, double linked list
  • Ideas you found here that could help you later: NIL
  • Ideas that didn’t work and why: NIL