</> MAANG.io
coding interview · 301

Advanced

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/95 solved 0% complete

Geometry and Sweep

What is this?

The Skyline Problem is the canonical sweep: sort every place where something changes, walk them in order, and keep a structure describing what is currently active. The output is emitted whenever that structure's summary changes.

Pour Water is the opposite temperament — a simulation with no data structure at all, where every difficulty is in stating the settling rule precisely enough to implement.

flowchart TD A["Geometry and Sweep"] --> B["Skyline: sweep events"] A --> C["Pour Water: settle one drop"] B --> B1["left edge → push (−h, right)"] B --> B2["right edge → the heap expires it lazily"] B1 --> B3["current max live height"] B2 --> B3 B3 --> B4["emit only when it CHANGES"] C --> C1["try left, then right, then here"] C1 --> C2["move to the lowest reachable,
never uphill"]

💡 Fun fact: the skyline sweep needs a max-heap that supports removing an arbitrary building when its right edge passes — an operation heaps do not have. The fix is the same lazy deletion used for priority queues with changing keys: push (−height, right_edge) and, before reading the top, discard any entry whose right edge is at or behind the current x. Entries die when they surface, not when they expire, and the answer is always correct at the moment it is read. The same trick appears in three separate chapters of this tier, which is a good reason to learn it once and properly.

🔓 The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: sort the events, keep a structure of what is live, and emit only when the summary changes. A sweep that emits on every event is not wrong so much as unreadable.


1. The sweep

events = []
for l, r, h in buildings:
    events.append((l, -h, r))                       # left edge: negative height sorts tallest first
    events.append((r, 0, 0))                        # right edge: a marker, no height
events.sort()

res, live = [], [(0, float('inf'))]                 # (−height, right edge); ground never expires
for x, negh, r in events:
    while live[0][1] <= x: heappop(live)            # lazy expiry
    if negh: heappush(live, (negh, r))
    h = -live[0][0]
    if not res or res[-1][1] != h: res.append([x, h])

Three details do all the work. Negative heights make a min-heap behave as a max-heap and simultaneously make taller buildings sort before shorter ones at the same x — which matters when two buildings start together. The ground sentinel (0, ∞) means the heap is never empty and the height falls to 0 naturally at the end of a run. And emitting only on change removes every duplicate point without a post-processing pass.

Right edges must be processed before left edges at the same x when the buildings do not overlap — which the (x, 0, 0) tuple gives for free, since 0 sorts after any negative height.

2. The simulation

Pour Water has no structure to choose. Each drop follows one rule:

  1. Move left while the ground keeps getting lower; if it reached a lower point, settle there.
  2. Otherwise try the same to the right.
  3. Otherwise settle where it fell.

"Keeps getting lower" is the part to state carefully — the drop may cross flat ground on its way to a lower point, but it may never go up, and it settles at the lowest position it can reach, breaking ties toward the left. Getting that ordering wrong produces plausible-looking output that is wrong on ridges and plateaus, which is exactly what the test cases probe.


3. A 30-second worked example (skyline)

buildings = [[2,9,10], [3,7,15], [5,12,12]]

events sorted:  (2,-10,9)  (3,-15,7)  (5,-12,12)  (7,0,0)  (9,0,0)  (12,0,0)

x=2   push (-10, 9)     live top = 10   ≠ nothing      emit [2, 10]
x=3   push (-15, 7)     live top = 15   ≠ 10           emit [3, 15]
x=5   push (-12, 12)    live top = 15   == 15          no emit
x=7   expire (-15, 7)   live top = 12   ≠ 15           emit [7, 12]
x=9   expire (-10, 9)   live top = 12   == 12          no emit
x=12  expire (-12,12)   live top = 0    ≠ 12           emit [12, 0]

skyline: [[2,10], [3,15], [7,12], [12,0]]

Two events emit nothing. At x=5 a new building starts but is hidden behind a taller one, and at x=9 a building ends without changing the visible height. Those are the two cases the "emit only on change" rule exists for — a sweep that appends unconditionally produces six points where four are correct.


4. Where you'll actually meet this

  • Graphics and rendering. Silhouette and occlusion computation is the skyline problem in two dimensions.
  • Resource utilisation charts. "How many things are active at time x" is the same sweep with counts instead of heights.
  • GIS and mapping. Horizon lines and viewshed analysis over elevation data.
  • Terrain and hydrology. Water settling models are Pour Water with more physics and the same logic.
  • Calendar and capacity views. Peak concurrent usage over a day is a sweep over start and end events.

5. Problems in this chapter

▶ The Skyline Problem

Outline of a set of overlapping rectangles. Sweep sorted edges with a max-heap of live heights, expiring entries lazily, and emit only when the height changes.
Pattern: sweep line + heap with lazy deletion. Target: O(n log n) time, O(n) space.

▶ Pour Water

Simulate water drops settling on a terrain. Each drop moves to the lowest reachable position, preferring left, never travelling uphill.
Pattern: rule-exact simulation. Target: O(drops × width) time, O(1) extra space.


6. Common pitfalls 🚫

  • Emitting a point at every event, which duplicates heights and fails the exact-output requirement.
  • Using positive heights in the heap, which gives a min-heap when a max-heap is needed and breaks same-x ordering.
  • Omitting the ground sentinel, so the heap empties and the final drop to zero is missed.
  • Trying to remove a specific building from the heap. Expire lazily on the way out.
  • Mis-ordering equal x-coordinates. Taller left edges must come first; right edges must come after.
  • Letting a water drop travel uphill to reach a lower point further along. It may not.
  • Breaking the water tie-break rightward. Left is preferred, and the tests check it.

7. Key takeaways

  1. Sort the events, then walk them. That is the whole sweep-line technique.
  2. Emit on change, not on event — it removes an entire post-processing step.
  3. Lazy deletion substitutes for a removal a heap does not have.
  4. Negating heights buys two things at once: max-heap behaviour and correct same-x ordering.
  5. A sentinel keeps the structure non-empty and makes the boundary case disappear.
  6. State a simulation's rule exactly before coding it. Ambiguity there is the bug.
  7. Why interviewers like it: the skyline is a well-known hard problem where the difficulty is entirely in the bookkeeping, which is where real code goes wrong too.

Order: The Skyline Problem → Pour Water.

Pour Water

Core idea: You're given an elevation map heights[] and you drop volume units of water one at a time at index k. There's no clever closed form here — the problem is the physics, and the whole skill is encoding the settling rule precisely. Each droplet obeys one deterministic rule. Look left first: walk from k as long as the ground does not rise (each next cell is <= the current one), remembering the lowest cell you can reach; if any cell strictly lower than k exists on that downhill walk, the droplet rolls there and that's where it lands. If the left walk found no lower resting spot, look right by the identical rule. If neither side offers a lower reachable spot, the droplet rests at k itself. Whichever cell wins, heights[that cell] += 1. Repeat for all volume drops. The single subtle bit is the left-before-right priority combined with "lowest reachable without ever climbing uphill" — get those two right and the rest is bookkeeping.


The problem, rephrased

Picture a cross-section of terrain as a bar chart — heights[i] is the ground level at column i. You stand above column k and pour water, one drop at a time. A drop doesn't teleport to the global minimum; it behaves like a real bead of water. It first tries to flow left, rolling downhill (or across flat ground) until it either reaches a dip it can settle into or hits a wall it can't climb. If rolling left lands it somewhere strictly lower than where it started, it settles there. If the left side offers no lower resting place, it tries the right side by the exact same downhill logic. And if both sides are walls or only-rising ground, the drop has nowhere lower to go, so it stays put at k. Wherever it ends up, the terrain at that column rises by one. The next drop sees the updated terrain and repeats.

Formally: given an integer array heights (the elevation map), an integer volume (number of water units to drop), and an integer k (the drop column), simulate all volume drops and return the final heights. This is LeetCode 755 — Pour Water.

Input Means Output
heights=[2,1,1,2,1,2,2], volume=4, k=3 Drops fall into the two dips flanking column 3 [2,2,2,3,2,2,2]
heights=[1,2,3,4], volume=2, k=2 Left/right both rise from k, so drops stack on k [2,3,3,4]
heights=[3,1,3], volume=5, k=2 Every drop rolls left into the single deep well at index 1 [4,4,4]

The rule reads simply but the order matters: left wins ties of "is there a lower spot", and "reachable" means you never step onto a cell higher than the one you're on.


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.