Bird
Raised Fist0
Interview Prepcustom-data-structureshardAmazonGoogle

Design Skiplist

Choose your preparation mode4 modes available
▶
Steps
setup

Initialize Skiplist

Create an empty skiplist with size 0, probability 0.5, head node with value -1 at level 0, and current max level 0.

💡 Initialization sets the foundation: head node acts as starting point for all levels, and size tracks number of elements for dynamic level calculation.
Line:self.size = 0 self.P = 0.5 self.head = Node(-1, 0) self.level = 0
💡 Skiplist starts empty with one level and a dummy head node to simplify insertions and searches.
📊
Design Skiplist - Watch the Algorithm Execute, Step by Step
Watching each pointer move and level adjustment live reveals the skiplist's layered linked list structure and probabilistic balancing, which is hard to grasp from code alone.
Step 1/20
·Active fill★Answer cell
setup
-1
traverse
-1
expand
-1
connect
-1
→
1
advance
-1
→
1
traverse
-1
→
1
expand
-1
→
1
expand
-1
→
1
connect
-1
→
1
→
2
advance
-1
→
1
→
2
traverse
-1
→
1
→
2
expand
-1
→
1
→
2
connect
-1
→
1
→
2
→
3
advance
-1
→
1
→
2
→
3
compare
-1
→
1
→
2
→
3
compare
-1
→
1
→
2
→
3
Result: false
traverse
-1
→
1
→
2
→
3
expand
-1
→
1
→
2
→
3
connect
-1
→
1
→
2
→
3
→
4
advance
-1
→
1
→
2
→
3
→
4

Key Takeaways

✓ Skiplist uses multiple levels of linked lists to achieve fast search, insert, and erase operations.

This layered structure is hard to visualize from code alone but becomes clear when watching pointer updates at each level.

✓ Node promotion via random level generation dynamically adjusts skiplist height for balance.

Seeing how random_level affects node insertion levels clarifies how skiplist probabilistically balances itself.

✓ Search and erase operations traverse top-down levels, skipping large sections efficiently.

Watching traversal pointers move at different levels reveals the skiplist's speed advantage over simple linked lists.

Practice

(1/5)
1. Given the following code snippet for the optimal UndergroundSystem implementation, what is the value of travel_time when checkOut(1, "Leyton", 20) is called after checkIn(1, "Paradise", 3) and checkIn(2, "Leyton", 10)?
class Node:
    def __init__(self, id, station, time):
        self.id = id
        self.station = station
        self.time = time
        self.next = None

class UndergroundSystem:
    def __init__(self):
        self.head = None
        self.route_data = {}

    def checkIn(self, id: int, stationName: str, t: int) -> None:
        new_node = Node(id, stationName, t)
        new_node.next = self.head
        self.head = new_node

    def checkOut(self, id: int, stationName: str, t: int) -> None:
        prev = None
        curr = self.head
        while curr:
            if curr.id == id:
                travel_time = t - curr.time
                route = (curr.station, stationName)
                if route not in self.route_data:
                    self.route_data[route] = [0, 0]
                self.route_data[route][0] += travel_time
                self.route_data[route][1] += 1
                if prev:
                    prev.next = curr.next
                else:
                    self.head = curr.next
                return
            prev = curr
            curr = curr.next
easy
A. 10
B. 17
C. 7
D. 20

Solution

  1. Step 1: Trace checkIn calls

    First, checkIn(1, "Paradise", 3) adds Node(id=1, station="Paradise", time=3) at head. Then checkIn(2, "Leyton", 10) adds Node(id=2, station="Leyton", time=10) at head.
  2. Step 2: Trace checkOut(1, "Leyton", 20)

    Traverse linked list from head: Node with id=2 (not match), then Node with id=1 (match). travel_time = 20 - 3 = 17.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-in time for id=1 is 3, checkout time is 20, difference is 17 [OK]
Hint: Subtract check-in time from checkout time for travel_time [OK]
Common Mistakes:
  • Using wrong node's time due to linked list order
2. Consider the following Python code implementing an LRU Cache with capacity 2. After executing the sequence of operations below, what is the return value of cache.get(1)?
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
cache.get(1)
cache.put(3, 3)
cache.get(2)
cache.put(4, 4)
cache.get(1)
cache.get(3)
cache.get(4)
easy
A. 1
B. 3
C. -1
D. 4

Solution

  1. Step 1: Trace cache state after each operation

    After put(1,1) and put(2,2), cache has keys [2 (MRU), 1 (LRU)]. get(1) moves key 1 to MRU, order: [1,2]. put(3,3) evicts LRU key 2, cache: [3,1]. get(2) returns -1 (evicted). put(4,4) evicts LRU key 1, cache: [4,3].
  2. Step 2: Evaluate get(1) after put(4,4)

    Key 1 was evicted, so get(1) returns -1.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Key 1 evicted after put(4,4), so get(1) -> -1 [OK]
Hint: Remember to update usage order on get and evict LRU on put [OK]
Common Mistakes:
  • Forgetting to move accessed node to front
  • Evicting wrong node
  • Returning wrong value after eviction
3. Examine the following buggy code snippet for the MedianFinder class. Which line contains the subtle bug that can cause incorrect median calculation?
medium
A. Condition 'if self.low_size > self.high_size:' for balancing heaps
B. Line that pushes to high heap and increments high_size
C. Line that pushes to low heap and increments low_size
D. Condition 'elif self.high_size > self.low_size + 1:' for balancing heaps

Solution

  1. Step 1: Understand balancing condition

    The heaps must be balanced so that their sizes differ by at most 1.
  2. Step 2: Identify incorrect condition

    The condition 'if self.low_size > self.high_size:' moves an element from low to high even when sizes differ by only 1, causing imbalance.
  3. Step 3: Correct condition

    The condition should be 'if self.low_size > self.high_size + 1:' to ensure size difference is more than 1 before balancing.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Balancing condition must check if low_size > high_size + 1 [OK]
Hint: Balance heaps only if size difference > 1 [OK]
Common Mistakes:
  • Balancing heaps too aggressively
  • Incorrect size difference checks
4. Examine the following buggy remove method from the optimized Insert Delete GetRandom O(1) with duplicates data structure. Which line contains the subtle bug that can cause incorrect behavior or runtime errors?
def remove(self, val: int) -> bool:
    if val not in self.idx_map or not self.idx_map[val]:
        return false
    remove_idx = self.idx_map[val].pop()
    last_val = self.nums[-1]
    self.nums[remove_idx] = last_val
    self.idx_map[last_val].add(remove_idx)
    # BUG: missing discard of old index for last_val
    self.nums.pop()
    if not self.idx_map[val]:
        del self.idx_map[val]
    return true
medium
A. Missing line to discard old index of last_val from idx_map[last_val]
B. Line adding remove_idx to idx_map[last_val] (self.idx_map[last_val].add(remove_idx))
C. Line popping remove_idx from idx_map[val] (remove_idx = self.idx_map[val].pop())
D. Line removing the last element from nums (self.nums.pop())

Solution

  1. Step 1: Understand index updates during removal

    When swapping last_val into remove_idx, we must update idx_map[last_val] by adding remove_idx and discarding the old index (len(nums)-1).
  2. Step 2: Identify missing discard

    The code adds remove_idx to idx_map[last_val] but does not discard the old index, causing stale indices and incorrect removals later.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Missing discard leads to incorrect index tracking [OK]
Hint: Always discard old indices after swapping during removal [OK]
Common Mistakes:
  • Forgetting to discard old index after swap
  • Removing wrong element from idx_map
  • Incorrect pop from idx_map[val]
5. Suppose the UndergroundSystem is extended to allow multiple check-ins by the same passenger without requiring a check-out first (i.e., passengers can start multiple trips before finishing any). Which modification to the data structure or algorithm is necessary to handle this correctly?
hard
A. Keep a stack of check-in nodes per passenger id to track multiple active trips.
B. Use a hash map from passenger id to a single check-in node, overwriting previous check-ins.
C. Store all check-ins in a linked list and traverse fully on each checkOut to find the earliest unmatched check-in.
D. Disallow multiple check-ins by the same passenger to avoid complexity.

Solution

  1. Step 1: Understand the problem extension

    Passengers can have multiple active trips simultaneously, so a single check-in per id is insufficient.
  2. Step 2: Choose data structure to track multiple check-ins

    A stack per passenger id allows tracking multiple check-ins in order, enabling correct matching on checkOut.
  3. Step 3: Why other options fail

    Overwriting check-ins loses data (A). Traversing full list each time (C) is inefficient. Disallowing multiple check-ins (D) is not a solution.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Stack per id supports multiple active trips correctly [OK]
Hint: Stack per id tracks multiple active check-ins [OK]
Common Mistakes:
  • Overwriting check-in info loses data
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard