The Skyline Problem

HardSweep Line · Heap · Divide & Conquer🧩 Intervals & Line SweepLeetCode #218 ↗Updated

Problem

Each building is a rectangle on flat ground given as [left, right, height]. Return the city's outline seen from far away as a list of key points [x, y] sorted by x: each point is where the outline jumps to height y, and the final point has y = 0. Two consecutive key points must never share the same height.
Input[[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]Output[[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]]
Input[[0,2,3],[2,5,3]]Output[[0,3],[5,0]]

1 ≤ n ≤ 104  ·  0 ≤ left < right ≤ 231-1  ·  1 ≤ height ≤ 231-1  ·  buildings sorted by left

Try it yourself

Java
class Solution {
    public List<List<Integer>> getSkyline(int[][] buildings) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
The outline can only change at a building edge, and its height there is simply the tallest building still covering that x. Sweep the edges left to right and keep the “currently standing” heights in a max-heap.
PatternIntervals & Line Sweep

Instinct See [start, end] pairs, meetings, bookings or the word "overlap"? Sort by an endpoint, then sweep left to right carrying one running fact.

Memory hack Sort, then sweep - start time to merge, end time to choose, +1/-1 events to count the crowd.

Open the Intervals & Line Sweep guide →

Approaches

Brute ForceCheck Every Edge Against Every Building

Time O(n²)Space O(n)

Only edge coordinates can be key points. For each distinct edge x (in order), scan all buildings and take the max height of those with left ≤ x < right. Emit [x, h] whenever h differs from the previous height.

Java
public List<List<Integer>> getSkyline(int[][] buildings) {
    TreeSet<Integer> xs = new TreeSet<>();
    for (int[] b : buildings) {
        xs.add(b[0]);
        xs.add(b[1]);
    }
    List<List<Integer>> res = new ArrayList<>();
    int prev = 0;
    for (int x : xs) {
        int h = 0; // tallest building covering [x, x+1)
        for (int[] b : buildings) {
            if (b[0] <= x && x < b[1]) h = Math.max(h, b[2]);
        }
        if (h != prev) {
            res.add(List.of(x, h));
            prev = h;
        }
    }
    return res;
}

Obviously correct and a great reference, but 2n × n work is too slow at scale.

At a glance

LevelApproachTimeSpace
Brute ForceCheck Every Edge Against Every BuildingO(n²)O(n)
BetterDivide & Conquer — Merge SkylinesO(n log n)O(n)
OptimalSweep Line + Max-Heap (Lazy Removal)O(n log n)O(n)
Common pitfalls
  • Emitting a point per edge without merging equal heights — adjacent same-height blocks give duplicates.
  • Reading the height between a start and an end at the same x → spurious dips.
  • Using heap.remove(obj) for ends — O(n) per removal; lazy deletion keeps O(log n).
Interview tips
  • Start from “the outline only changes at an edge” — it justifies the brute force and the sweep.
  • Say what the heap stores ({height, right}) and why lazy expiry is safe.
  • Mention a TreeMap height-count multiset as the alternative with eager removal.
Pattern
Sweep line + max-heap → “what is the max over all intervals covering x” as x moves across sorted event points.
My notes saved in this browser