Bird
Raised Fist0
Interview Prepcustom-data-structureshardAmazonGoogle

Design Skiplist

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 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 fillAnswer 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. Consider the following Python code snippet for the MedianFinder class using two heaps. After adding the numbers 1, 5, and 3 in that order, what is the value of the median returned by findMedian()?
easy
A. 1.0
B. 3.0
C. 5.0
D. 2.0

Solution

  1. Step 1: Insert 1

    Low heap empty, push -1 to low; low_size=1, high_size=0.
  2. Step 2: Insert 5

    5 > -low[0] (which is 1), push 5 to high; low_size=1, high_size=1; heaps balanced.
  3. Step 3: Insert 3

    3 <= 5 but > 1, push 3 to high; high_size=2, low_size=1; balance heaps by moving smallest from high (3) to low (-3); low_size=2, high_size=1.
  4. Step 4: Find median

    low_size > high_size, median is -low[0] = 3.0.
  5. Step 5: Re-examine median calculation

    After balancing, low heap has [-3, -1], high heap has [5]. Median is top of low heap = 3.0, but the question asks for median after adding 1,5,3 in order, so median is 3.0.
  6. Final Answer:

    Option B -> Option B
  7. Quick Check:

    Median after [1,5,3] is 3.0 [OK]
Hint: Median is top of larger heap after balancing [OK]
Common Mistakes:
  • Forgetting to balance heaps after insertion
  • Returning wrong heap top for median
2. 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
3. Consider the following buggy LFUCache get method. Which line contains the subtle bug that can cause incorrect eviction behavior?
def get(self, key: int) -> int:
    if key not in self.key_val_freq:
        return -1
    val, freq = self.key_val_freq[key]
    del self.freq_keys[freq][key]
    if not self.freq_keys[freq]:
        del self.freq_keys[freq]
        # BUG: missing update of min_freq here
    self.freq_keys[freq + 1][key] = None
    self.key_val_freq[key] = (val, freq + 1)
    return val
What is the bug?
medium
A. Missing update of min_freq after deleting freq_keys[freq]
B. Line deleting freq_keys[freq] when empty
C. Line deleting key from freq_keys[freq]
D. Line adding key to freq_keys[freq + 1]

Solution

  1. Step 1: Identify the role of min_freq

    min_freq tracks the smallest frequency present in the cache, needed for correct eviction.
  2. Step 2: Check frequency list deletion

    When freq_keys[freq] becomes empty and is deleted, if freq equals min_freq, min_freq must be incremented to freq + 1.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Missing min_freq update causes stale min_freq and wrong eviction [OK]
Hint: min_freq must update when freq list empties [OK]
Common Mistakes:
  • Forgetting to update min_freq after freq list deletion
  • Confusing key deletion with frequency list deletion
  • Assuming min_freq updates automatically
4. Suppose the AllOne data structure is extended to allow keys to be reused after removal (i.e., keys can be reinserted after their count reaches zero). Which modification is necessary to maintain O(1) operations and correctness?
hard
A. Do nothing special; existing code handles reuse correctly
B. Maintain a separate reuse queue to track keys that can be reinserted
C. Reset key's count to zero and keep it in the data structure to track reuse
D. Ensure keys are fully removed from all buckets and mappings upon count zero, then treat reuse as new insertion

Solution

  1. Step 1: Understand reuse implications

    Keys removed at count zero must be fully deleted to avoid stale references.
  2. Step 2: Ensure reuse is treated as new insertion

    On reuse, key is inserted fresh with count 1, requiring clean removal previously.
  3. Step 3: Confirm O(1) operations

    Full removal and fresh insertion maintain O(1) updates and correctness.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Proper removal prevents duplicates and stale buckets [OK]
Hint: Full removal before reuse ensures correctness and O(1) ops [OK]
Common Mistakes:
  • Keeping keys with zero count in data structure causes bugs
5. Suppose the LFU cache must support a new operation: reuse(key) which resets the frequency of key to 1 without changing its value. Which modification to the optimal LFU cache design correctly supports this operation without breaking O(1) complexity?
hard
A. Simply set the frequency of the key to 1 in the key_val_freq map without moving it between frequency lists
B. Clear the entire cache and reinsert the key with frequency 1
C. Remove the key from its current frequency list and add it to freq=1 list, update min_freq to 1
D. Increment the frequency of the key by 1 and then reset min_freq to 1

Solution

  1. Step 1: Understand reuse operation

    Reuse resets frequency to 1, so the key must move from its current frequency list to the freq=1 list.
  2. Step 2: Update data structures accordingly

    Remove key from old freq list, add to freq=1 list, and update min_freq to 1 to reflect the new minimum frequency.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Moving key between freq lists and updating min_freq preserves O(1) operations [OK]
Hint: Move key to freq=1 list and update min_freq [OK]
Common Mistakes:
  • Not moving key between frequency lists
  • Resetting frequency without updating min_freq
  • Clearing cache is inefficient and breaks O(1)