Bird
Raised Fist0
Interview Prepcustom-data-structureshardAmazonGoogleFacebook

Insert Delete GetRandom O(1) with Duplicates

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Initialize empty data structure

Create an empty list 'nums' and empty maps 'idx_map' and 'node_map'. No values or nodes exist yet.

💡 Starting with empty structures is essential to see how insertions build the data structure incrementally.
Line:self.nums = [] self.idx_map = {} self.node_map = {}
💡 The data structure starts empty, ready to store values and their index nodes.
📊
Insert Delete GetRandom O(1) with Duplicates - Watch the Algorithm Execute, Step by Step
Watching each pointer update and list modification reveals how the algorithm efficiently handles duplicates and maintains index mappings, which is hard to grasp from code alone.
Step 1/11
·Active fillAnswer cell
advance
insert
0
Result: [1]
insert
0
1
Result: [1, 1]
insert
0
1
2
Result: [1, 1, 2]
compare
0
1
2
Result: [1, 1, 2]
delete
0
1
2
Result: [1, 1, 2]
detach
0
1
2
Result: [1, 1, 2]
connect
2
1
2
Result: [2, 1, 2]
advance
0
1
Result: [2, 1]
shrink
0
1
Result: [2, 1]
compare
0
1
Result: [2, 1]

Key Takeaways

Using a doubly linked list per value to track indices allows O(1) removal of any occurrence of duplicates.

This insight is hard to see from code alone because the linked list structure and its role in index tracking are implicit.

Swapping the last element into the removed index keeps the main array compact and enables O(1) getRandom.

Visualizing the swap clarifies why no shifting is needed and how indices must be updated.

getRandom simply picks a random index from the main list, reflecting the current multiset distribution.

Seeing the random pointer move helps understand the probabilistic output.

Practice

(1/5)
1. You need to design a system that tracks passengers checking in and out of stations, and efficiently calculates average travel times between stations. Which approach best balances time and space efficiency for frequent queries?
easy
A. Use hash maps to store running totals of travel times and counts per route.
B. Store all trips explicitly and scan them to compute averages on demand.
C. Use dynamic programming to precompute all possible route averages.
D. Use a priority queue to keep track of the shortest travel times per route.

Solution

  1. Step 1: Understand the problem requirements

    The system must support frequent check-ins, check-outs, and quick average time queries between stations.
  2. Step 2: Evaluate approaches

    Storing all trips (B) leads to slow queries. Dynamic programming (C) is not applicable as routes are dynamic and not fixed. Priority queues (D) do not help compute averages efficiently. Hash maps with running totals (A) allow O(1) updates and queries.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Hash maps maintain running sums and counts for O(1) average time queries [OK]
Hint: Running totals enable O(1) average queries [OK]
Common Mistakes:
  • Thinking storing all trips is efficient for queries
2. You are given a list of buildings represented as triplets (left, right, height). The goal is to output the skyline formed by these buildings as a list of key points where the height changes. Which algorithmic approach guarantees an optimal solution with time complexity O(n log n)?
easy
A. Divide and conquer approach that recursively merges skylines of building subsets
B. Dynamic programming approach that stores maximum heights for subproblems
C. Greedy approach that iteratively picks the tallest building at each x-coordinate
D. Brute force simulation by iterating over all x-coordinates and tracking max height

Solution

  1. Step 1: Understand problem requirements

    The problem requires merging multiple building outlines into a single skyline efficiently.
  2. Step 2: Evaluate algorithmic approaches

    Greedy and brute force approaches either fail to guarantee optimal time or are inefficient. Dynamic programming is not a natural fit here. Divide and conquer merges smaller skylines efficiently in O(n log n) time.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Divide and conquer merges sorted skylines efficiently [OK]
Hint: Divide and conquer merges sorted skylines in O(n log n) [OK]
Common Mistakes:
  • Thinking greedy or brute force is optimal
  • Confusing DP with skyline merging
3. Identify the bug in the following LRU Cache implementation snippet that causes incorrect eviction behavior when capacity is exceeded:
def put(self, key: int, value: int) -> None:
    if key in self.cache:
        self._remove(self.cache[key])
    node = Node(key, value)
    self._add(node)
    self.cache[key] = node
    if len(self.cache) > self.capacity:
        lru = self.head.next  # Bug here
        self._remove(lru)
        del self.cache[lru.key]
medium
A. Evicting the node at head.next instead of tail.prev
B. Removing node from cache before adding new node
C. Not updating the cache dictionary after eviction
D. Not checking if capacity is zero before eviction

Solution

  1. Step 1: Understand eviction logic

    Eviction must remove the least recently used node, which is at tail.prev, not head.next.
  2. Step 2: Identify bug

    Code incorrectly evicts head.next, which is the most recently used node, causing wrong eviction.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Evicting head.next breaks LRU policy, must evict tail.prev [OK]
Hint: LRU node is at tail.prev, not head.next [OK]
Common Mistakes:
  • Evicting MRU instead of LRU
  • Forgetting to delete from cache dict
  • Not handling zero capacity
4. Suppose the data stream allows duplicate numbers to be removed after insertion, requiring support for removeNum(num). Which modification to the balanced two heaps approach is necessary to maintain correct median retrieval?
hard
A. Use a balanced binary search tree instead of heaps to allow efficient removals.
B. Ignore removals and only add numbers, as removals break the heap structure.
C. Rebuild both heaps from scratch after every removal to maintain heap properties.
D. Add a delayed deletion map to mark elements for lazy removal and prune heaps during addNum and findMedian.

Solution

  1. Step 1: Understand removal challenges

    Heaps do not support efficient arbitrary removals, so direct removal breaks heap properties.
  2. Step 2: Use lazy deletion

    Maintain a delayed deletion map to mark elements for removal and prune them lazily during heap operations, preserving heap structure and efficiency.
  3. Step 3: Avoid costly rebuilds

    Rebuilding heaps on every removal is inefficient; ignoring removals is incorrect.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Lazy deletion with pruning maintains correctness and efficiency [OK]
Hint: Lazy deletion map enables efficient removals in heaps [OK]
Common Mistakes:
  • Rebuilding heaps on every removal
  • Ignoring removals breaks correctness
5. Suppose the multilevel doubly linked list can have cycles introduced via child pointers (i.e., a child pointer may point to an ancestor node). Which modification to the flattening algorithm is necessary to handle this safely?
hard
A. Use recursion with a depth limit to prevent stack overflow on cycles.
B. Remove all child pointers before flattening to guarantee acyclic structure.
C. Use a hash set to track visited nodes and skip already visited ones to avoid infinite loops.
D. No modification needed; the existing in-place iterative approach handles cycles naturally.

Solution

  1. Step 1: Understand the problem with cycles

    Cycles cause infinite loops during traversal if nodes are revisited.
  2. Step 2: Identify safe traversal method

    Tracking visited nodes with a hash set prevents revisiting and infinite loops.
  3. Step 3: Evaluate other options

    Removing child pointers loses structure; recursion depth limit is unreliable; existing code does not detect cycles.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Visited set prevents infinite loops in cyclic graphs [OK]
Hint: Track visited nodes to handle cycles safely [OK]
Common Mistakes:
  • Assuming no cycles exist in input
  • Relying on recursion depth limits
  • Ignoring cycle detection altogether