Implement weighted-eviction cache
Company: Netflix
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding and practical implementation of data structures and algorithms for a weighted cache, including weighted eviction policies, use of ordered maps, and time-space complexity analysis.
Constraints
- 0 <= len(operations) <= 200000
- 0 <= capacity <= 10^12
- Operations are arrays: ["put", key, value, weight] or ["get", key]
- Keys and values are 32-bit signed integers
- 1 <= weight <= 10^9 for put operations
- If weight > capacity, the put is ignored and the cache is unchanged
- On put of an existing key, first replace its value and weight, then apply eviction
- When evicting due to capacity overflow, evict exactly one key: the key with the largest weight; ties broken by smallest key
- Return "null" for every put operation; return the stored value or -1 for get
Solution
from typing import List
import heapq
def weighted_eviction_cache(operations: list[list], capacity: int) -> list:
# Max-heap via negatives: entries are (-weight, key, version)
heap = []
store = {} # key -> (value, weight, version)
total = 0
version = 0
res: List[object] = []
def push(key: int, value: int, weight: int):
nonlocal version, total
version += 1
store[key] = (value, weight, version)
heapq.heappush(heap, (-weight, key, version))
total += weight
def evict_once_if_needed():
nonlocal total
if total <= capacity:
return
# Pop until we find a valid (non-stale) top; evict it.
while heap:
neg_w, k, ver = heapq.heappop(heap)
w = -neg_w
cur = store.get(k)
if cur is None or cur[2] != ver:
continue # stale
# Evict this key
total -= w
del store[k]
break
for op in operations:
if not op:
res.append("null")
continue
typ = op[0]
if typ == "put":
_, key, value, weight = op
if weight > capacity:
# Ignore this put entirely
res.append("null")
continue
# If key exists, remove its old weight from total; the old heap entry becomes stale
old = store.get(key)
if old is not None:
total -= old[1]
push(key, value, weight)
evict_once_if_needed()
res.append("null")
elif typ == "get":
_, key = op
cur = store.get(key)
res.append(cur[0] if cur is not None else -1)
else:
# Unknown op type; ignore
res.append("null")
return res
Explanation
Time complexity: Amortized O(log N) per put (heap push and at most one valid pop), O(1) per get; stale pops are amortized. N is the number of keys in the cache.. Space complexity: O(N) for the map and heap (heap may contain stale entries proportional to the number of updates)..
Hints
- Maintain key -> (value, weight, version) in a hash map for O(1) access.
- Use a max-priority structure to find the largest weight quickly; in Python, use a min-heap with negative weights.
- To break ties by smallest key, include the key as the second component of the heap tuple.
- Handle updates by storing a version/timestamp per key and marking old heap entries as stale.
- Subtract the old weight before adding the new weight on put; if overweight, evict the current maximum.