Question presented to candidate: "Design an LRU cache class in Python with get(key) and put(key, value), both O(1). When it is full, put must evict the least recently used key. Then tell me what you would change for a real service."
What a strong answer should cover:
- LRU definition and the two jobs: O(1) lookup by key and O(1) maintenance of recency order.
- The standard structure: a dict from key to node plus a doubly linked list ordered by recency, with eviction at the old end.
- Both
getandputcount as a use; updating an existing key never evicts; a miss leaves the order alone. - The Python shortcut
collections.OrderedDictwithmove_to_end(key)andpopitem(last=False), and why a plain dict is awkward (assigning to an existing key does not move it). - Beyond the coding round: LRU vs FIFO/LFU, thrashing on scans, locks for threads, TTL, and
functools.lru_cachefor memoising functions.
Clarifying questions expected:
- Is capacity counted in entries or bytes, and can it be 0?
- Does
puton an existing key count as a use? - Is the cache shared between threads?
Code / implementation expected: Yes โ an LRUCache class in Python 3, using OrderedDict or a hand-written dict plus doubly linked list.