Skip to content

205 Implement LRU Cache (Lai)

Implement a least recently used cache. It should provide set(), get() operations. If not exists, return null (Java), false (C++), -1(Python).

Solution:

public class Solution<K, V> {
  // limit is the max capacity of the cache
  static class Node<K,V>{
    K key;
    V value;
    Node<K, V> next;
    Node<K, V> prev;
    Node(K key, V value){
      this.key = key;
      this.value = value;
    }
  }

  private Map<K, Node<K, V>> map = new HashMap<K, Node<K, V>>();
  private int size;
  private int limit;
  private Node<K, V> head;
  private Node<K, V> tail;

  public Solution(int limit) {
    this.size = 0;
    this.limit = limit;
    head = new Node(-1, -1);
    tail = new Node(-1, -1);
    head.next = tail;
    tail.prev = head;
  }

  public void set(K key, V value) {
    Node node = map.get(key);
    if (node == null){
      node = new Node(key, value);
      map.put(key, node);
      size = size + 1;
      addToHead(node);
      if (size > limit){
        Node<K,V> tailNode = removeTail();
        map.remove(tailNode.key);
        size = size - 1;
      }
    }else{
      node.value = value;
      moveToHead(node);
    }
  }

  private Node<K, V> removeTail(){
    Node<K, V> tailNode = tail.prev;
    removeNode(tailNode);
    return tailNode;
  }

  private void moveToHead(Node<K, V> node){
    removeNode(node);
    addToHead(node);
  }

  private void removeNode(Node<K, V> node){
    node.prev.next = node.next;
    node.next.prev = node.prev;
  }

  private void addToHead(Node<K, V> node){
    node.prev = head;
    node.next = head.next;
    head.next.prev = node;
    head.next = node;
  }

  public V get(K key) {
    Node<K, V> node = map.get(key);
    if (node == null){
      return null;
    }
    moveToHead(node);
    return node.value;
  }
}
public class Solution<K, V> {
  // limit is the max capacity of the cache
  static class Node<K, V>{
    K key;
    V value;
    Node<K, V> next;
    Node<K, V> prev;
    Node(K key, V value){
      this.key = key;
      this.value = value;
    }

    void update(K key, V value){
      this.key = key;
      this.value = value;
    }
  }

  // make it final for the pre-defined size limit of the cache.
  private final int limit;
  // record all the time the head and tail of the double linked list.
  private Node<K, V> head;
  private Node<K, V> tail;

  // maintains the relationship of key and its corresponding node
  // in the double linked list
  private Map<K, Node<K, V>> map;

  public Solution(int limit) {
    this.limit = limit;
    this.map = new HashMap<K, Node<K,V>>();
  }

  public void set(K key, V value) {
    Node<K, V> node = null;
    // 1. if the key already in the cache, we need to update its value
    // and move it to head(most recent position)
    if (map.containsKey(key)){
      node = map.get(key);
      node.value = value;
    }else if (map.size() < limit){
      node = new Node<K, V>(key, value);
    }else{
      // 3. if the key is not in the cache and we don't have space,
      // we need to evict the tail and reuse the node let it maintain 
      // the new key, value and put it to head.
      node = tail;
      remove(node);
      node.update(key, value);
    }
    append(node);
  }



  private Node<K, V> append(Node<K, V> node){
    map.put(node.key, node);
    if (head == null){
      head = tail = node;
    }else{
      node.next = head;
      head.prev = node;
      head = node;
    }
    return node;
  }

  public V get(K key) {
    Node<K, V> node = map.get(key);
    if (node == null){
      return null;
    }
    // even it is a read operating, it is still a write operation to 
    // the double linked list, and we need to move the node to head.
    remove(node);
    append(node);
    return node.value;
  }

  private Node<K, V> remove(Node<K, V> node){
    map.remove(node.key);
    if (node.prev != null){
      node.prev.next = node.next;
    }

    if (node.next != null){
      node.next.prev = node.prev;
    }

    if (node == head){
      head = head.next;
    }

    if (node == tail){
      tail = tail.prev;
    }

    node.next = node.prev = null;
    return node;
  }
}

// Time: O(1) for all operations