Coding problem · Medium · Design & Data Structures

LRU Cache

Medium Practical tasks Most askedSeen at Amazon, Meta, Microsoft, Bloomberg, Uber

Design a Least Recently Used (LRU) cache — the eviction policy behind many in-process caches.

  • LRUCache(capacity) creates a cache holding at most capacity keys.
  • get(key) returns the value, or -1 if absent. A successful get makes the key most recently used.
  • put(key, value) inserts or updates the key (making it most recently used). If this exceeds capacity, evict the least recently used key.

Both operations must run in O(1) average time.

Example 1

Input: ["LRUCache","put","put","get","put","get","put","get","get","get"]
[[2],[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]]
Output: [null,null,null,1,null,-1,null,-1,3,4]

Hints

Hint 1

You need O(1) lookup (hash map) and O(1) 'move to most recent' / 'remove least recent' (doubly linked list).

Hint 2

Many languages have an insertion-ordered map you can lean on: JavaScript Map, Python OrderedDict, Java LinkedHashMap, PHP arrays.

Write and run your solution

Loading the editor… (JavaScript is required to run code in your browser.)

Solution and explanation

Show the solution

Time O(1) per operation · Space O(capacity)

Pair a hash map (key → node) with a doubly linked list ordered by recency. get moves the node to the front; put updates or inserts at the front and, when over capacity, removes the tail node and its map entry. Every step is O(1).

In interviews it's fine to show the language shortcut first — JavaScript's Map and Python's OrderedDict keep insertion order, so "delete and re-insert" moves a key to the most-recent end — then explain (or implement) the linked-list version, as the Java reference does with LinkedHashMap(accessOrder = true).

Reference solution

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map(); // insertion order = recency order (oldest first)
  }

  get(key) {
    if (!this.map.has(key)) return -1;
    const value = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, value);
    return value;
  }

  put(key, value) {
    this.map.delete(key);
    this.map.set(key, value);
    if (this.map.size > this.capacity) this.map.delete(this.map.keys().next().value);
  }
}

Common mistakes

  • Forgetting that get also updates recency.
  • Updating an existing key without moving it to most-recent.
  • Evicting before checking whether the key already exists.

Follow-up questions

  • Make it thread-safe.
  • Add TTL expiry (see the TTL cache task).
  • LFU cache: evict the least frequently used key.