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
Start: Divide the buildings list
The algorithm begins by checking the input buildings list length. Since there are multiple buildings, it splits the list into two halves for recursive processing.
💡 Dividing the problem reduces complexity by handling smaller building groups separately before merging.
Line:mid = len(buildings) // 2
left = getSkyline(buildings[:mid])
right = getSkyline(buildings[mid:])
💡 Divide and conquer breaks the problem into manageable subproblems.
setup
Base case: Compute skyline for single building [2,9,10]
The left half is further divided until a single building [2,9,10] is isolated. The skyline for this building is created as two points: start height and end zero.
💡 Base case returns a simple skyline for one building, forming the foundation for merges.
The Skyline Problem - Watch the Algorithm Execute, Step by Step
Watching each pointer move and merge decision reveals how the skyline is constructed from overlapping buildings, making the complex merging logic intuitive.
Step 1/20
·Active fill★Answer cell
setup
[2,9,10]
0
[3,7,15]
1
[5,12,12]
2
[15,20,10]
3
[19,24,8]
4
Result: []
setup
0
1
Result: [[2,10],[9,0]]
setup
0
1
Result: [[3,15],[7,0]]
setup
j
0
1
Result: []
move_left
j
0
i
1
Result: [[2,10]]
move_right
0
j
1
Result: [[2,10],[3,15]]
move_right
0
i
1
Result: [[2,10],[3,15],[7,0]]
record
0
Result: [[2,10],[3,15],[7,0],[9,0]]
setup
[5,12,12]
0
[15,20,10]
1
[19,24,8]
2
Result: []
setup
0
1
Result: [[5,12],[12,0]]
setup
0
1
Result: [[15,10],[20,0]]
setup
0
1
Result: [[19,8],[24,0]]
setup
j
0
1
Result: []
move_left
j
0
i
1
Result: [[15,10]]
move_right
0
j
1
Result: [[15,10],[19,8]]
move_left
0
Result: [[15,10],[19,8],[20,0]]
record
0
Result: [[15,10],[19,8],[20,0],[24,0]]
setup
j
0
1
2
3
Result: []
move_left
j
0
i
1
2
3
Result: [[2,10]]
move_left
j
0
1
i
2
Result: [[2,10],[3,15]]
Key Takeaways
✓ Divide and conquer breaks the complex skyline problem into manageable subproblems.
This insight is hard to see from code alone because recursion and merging are intertwined.
✓ Merging skylines requires careful pointer movement and height tracking to correctly combine contours.
Visualizing pointer moves and height updates clarifies how overlapping buildings affect the skyline.
✓ Height changes in the merged skyline occur only when the maximum height between left and right skylines changes.
This explains why some points are skipped and others recorded, which is subtle in code but clear in visualization.
Practice
(1/5)
1. Given the following code snippet for the optimal Twitter design, what is the output of getNewsFeed(1) after these operations?
Assume getNewsFeed returns a list of tweetIds sorted from most recent to oldest.
easy
A. [6]
B. [5, 6]
C. [5]
D. [6, 5]
Solution
Step 1: Trace postTweet calls
User 1 posts tweet 5 at time=1, user 2 posts tweet 6 at time=2.
Step 2: Trace getNewsFeed(1)
User 1 follows user 2, so heap contains tweets with times 1 (tweet 5) and 2 (tweet 6). Max heap pops tweet 6 first, then tweet 5.
Final Answer:
Option D -> Option D
Quick Check:
Heap orders tweets by descending time, so output is [6, 5] [OK]
Hint: Heap orders tweets by negative time for max behavior [OK]
Common Mistakes:
Returning tweets in posting order instead of time order
Ignoring followees' tweets
2. You need to design a data structure that supports adding numbers from a stream and retrieving the median efficiently at any time. Which approach best balances insertion and median retrieval time for large data streams?
easy
A. Maintain two heaps: a max-heap for the lower half and a min-heap for the upper half, balancing their sizes after each insertion.
B. Sort the entire list of numbers every time you query the median.
C. Use a balanced binary search tree to keep all numbers sorted and find the median by in-order traversal.
D. Store numbers in an unsorted array and scan it fully to find the median on each query.
Solution
Step 1: Understand the problem constraints
We need efficient insertion and median retrieval for a data stream, so sorting on every query (Sort the entire list of numbers every time you query the median.) or scanning unsorted arrays (Store numbers in an unsorted array and scan it fully to find the median on each query.) is too slow.
Step 2: Evaluate data structures
Using two heaps (max-heap for lower half, min-heap for upper half) allows O(log n) insertion and O(1) median retrieval by balancing sizes, which is optimal for streaming data.
Final Answer:
Option A -> Option A
Quick Check:
Two heaps balance halves efficiently for median [OK]
Hint: Two heaps balance halves for O(log n) insert and O(1) median [OK]
Common Mistakes:
Sorting on every query is too slow for streams
Using one heap only cannot track median efficiently
3. Given the following addRange method snippet from the optimal RangeModule implementation, what is the value of self.starts after calling addRange(2, 6) on an initially empty RangeModule?
def addRange(self, left: int, right: int) -> None:
i = bisect_left(self.starts, left)
j = bisect_right(self.starts, right)
if i != 0 and self.intervals[self.starts[i-1]] >= left:
i -= 1
left = min(left, self.starts[i])
if j != 0:
right = max(right, self.intervals[self.starts[j-1]])
for k in self.starts[i:j]:
del self.intervals[k]
self.starts[i:j] = [left]
self.intervals[left] = right
Since i == 0, the first if condition is skipped. Also, j == 0, so second if is skipped. No intervals to delete. Insert left=2 at self.starts[0:0], so self.starts = [2]. Interval stored as {2:6}.
Final Answer:
Option C -> Option C
Quick Check:
Starts list after first addRange is [2] [OK]
Hint: Bisect on empty list returns 0, so starts list gets single element [OK]
Common Mistakes:
Assuming right endpoint is also inserted in starts list
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
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).
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.
Final Answer:
Option A -> Option A
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 you want to modify the skiplist to support duplicate values and allow searching for the k-th occurrence of a target. Which modification is the most appropriate to maintain efficient search and insertion?
hard
A. Insert duplicate nodes at the lowest level only, ignoring higher levels
B. Store a count of duplicates in each node and update counts on insert/erase
C. Use a hash map to track occurrences separately from the skiplist
D. Disallow duplicates and reject insertions of existing values
Solution
Step 1: Understand duplicate handling
To support duplicates and k-th occurrence search, nodes must track counts or occurrences.
Step 2: Evaluate options
Storing counts in nodes and updating them maintains skiplist structure and efficient operations.
Step 3: Reject other options
Inserting duplicates only at lowest level breaks skiplist invariants; hash map adds overhead; disallowing duplicates contradicts requirement.
Final Answer:
Option B -> Option B
Quick Check:
Counting duplicates in nodes preserves skiplist efficiency and supports k-th occurrence [OK]
Hint: Count duplicates in nodes to handle multiple occurrences efficiently [OK]
Common Mistakes:
Ignoring duplicates in higher levels
Using external structures breaking skiplist properties