Design O(1) insert/delete and frequency-weighted random
Company: LinkedIn
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates data structure design skills around maintaining multiset state with average O(1) insertions and deletions while supporting frequency-weighted random sampling, testing concepts such as hashing, index management, and randomized selection under duplicates.
Constraints
- 1 <= len(operations) == len(values) <= 2 * 10^5
- Each operation is one of "add", "delete", or "random"
- -10^9 <= values[i] <= 10^9
- Target complexity is O(1) average time per operation
Examples
Input: (["add", "add", "add", "random", "delete", "random"], [5, 10, 5, 2, 5, 1])
Expected Output: [True, True, False, 5, True, 10]
Explanation: After inserting 5, 10, and 5, the internal slots are [5, 10, 5]. random(2) returns slots[2] = 5. delete(5) removes the most recently inserted remaining 5, leaving [5, 10]. random(1) then returns 10.
Input: (["add", "add", "add", "delete", "random", "add", "random"], [1, 2, 1, 2, 1, 3, 4])
Expected Output: [True, True, False, True, 1, True, 1]
Explanation: Deleting 2 removes it from the middle, so the last 1 is swapped into its slot. The structure becomes [1, 1], so random(1) returns 1. After adding 3, random(4) uses 4 % 3 = 1, which is still 1.
Input: (["delete", "random", "add", "add", "random", "delete", "random", "delete", "random"], [7, 0, -1, -1, 3, -1, 0, -1, 5])
Expected Output: [False, None, True, False, -1, True, -1, True, None]
Explanation: This checks edge cases: deleting a missing value, calling random on an empty collection, handling duplicates, and storing negative numbers.
Input: (["add", "add", "add", "add", "add", "delete", "delete", "random", "delete", "random"], [1, 2, 1, 3, 1, 2, 3, 2, 1, 1])
Expected Output: [True, True, False, True, False, True, True, 1, True, 1]
Explanation: After deleting 2, the newest 1 is swapped forward, which means the per-value position list for 1 is no longer sorted by slot index. A correct O(1) solution must still update bookkeeping properly when deleting 1 later.
Solution
def solution(operations, values):
if len(operations) != len(values):
raise ValueError("operations and values must have the same length")
positions = {}
data = []
result = []
for op, val in zip(operations, values):
if op == "add":
if val not in positions:
positions[val] = []
existed = len(positions[val]) > 0
positions[val].append(len(data))
data.append([val, len(positions[val]) - 1])
result.append(not existed)
elif op == "delete":
if val not in positions or not positions[val]:
result.append(False)
continue
remove_idx = positions[val].pop()
last_val, last_pos = data[-1]
last_idx = len(data) - 1
if remove_idx != last_idx:
data[remove_idx] = [last_val, last_pos]
positions[last_val][last_pos] = remove_idx
data.pop()
if not positions[val]:
del positions[val]
result.append(True)
elif op == "random":
if not data:
result.append(None)
else:
idx = val % len(data)
result.append(data[idx][0])
else:
raise ValueError("Unknown operation: " + str(op))
return resultTime complexity: O(n) total for n operations, which is O(1) average per operation. Space complexity: O(n).
Hints
- If every occurrence is stored in an array, then frequency-weighted random becomes choosing one array slot.
- For O(1) deletion, swap the removed slot with the last slot. To update bookkeeping in O(1), store for each slot both its value and where that slot is recorded inside that value's position list.