Case File #150 · LeetCode #218 · Hard

Case File #150: Solving The Skyline Problem With CCF

Hard problems need richer answers, not necessarily more questions. Stop guessing patterns. Start proving solutions.

Hard problems need richer answers, not necessarily more questions. Skyline becomes lawful once movement and persistence are proved.

Why Skyline Feels Difficult

It is tempting to begin with “Sweep Line plus Heap.” CCF begins with what must be proven: the answer, the evidence that can change it, the active scope, and what must survive movement.

Reasoning Stack: Answer Space → Problem Space → Decision Space → Transcription.
After deriving a lawful route, audit it for unnecessary state and work.

Problem Statement

Given buildings [left, right, height] sorted by left boundary, construct the visible outer contour as ordered skyline key points [x, height]. Emit a point only when the visible height changes.

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

x:        2   3       7   9       12
building:  [----10-----]
             [----15----]
                 [-------12-------]
skyline:  [2,10] [3,15] [7,12] [12,0]

Answer Space

question: What must be returned, and what makes one result point lawful?

evidence: The requested result is a sequence of critical skyline changes, not building identities or every sampled coordinate.

reasoning: A point needs its horizontal location and visible height. Repeating an adjacent height adds no contour information.

answer: Answer Space: Array<[x, height]>, ordered by x, with only real height transitions and a final return to zero.

next: What input evidence can change that visible height?

The example emits [2,10], [3,15], [7,12], and [12,0]; it never emits duplicate height 15.

Problem Space

question: What evidence and relationship determine visibility?

evidence: Each building supplies left, right, and height. Buildings overlap, and input left boundaries are already ordered.

reasoning: At any x, the contour is the maximum height of buildings whose left is at or before x and whose right is after x. Only starts and ends can alter that maximum.

answer: Problem Space: an ordered Array<Building>; active overlap and the maximum active height are the decisive relationships.

next: What are we constructing from that evidence?

At x=3, heights 10 and 15 are active, so 15 is visible. At x=7, height 15 expires and height 12 becomes visible.

Decision Space

Intent

question: What semantic job does the problem require?

evidence: The verb is construct, the subject is the visible outer contour, and the result is a complete ordered sequence of key points.

reasoning: This is not merely finding a maximum at one coordinate. The result assembles every meaningful change into one lawful contour.

answer: Intent: Construction / Assembly.

next: What truth must remain true after every relevant coordinate?

Each emitted point extends a single contour; the final [12,0] closes it instead of returning an isolated maximum.

Invariant

question: What truth protects a correct constructed contour?

evidence: At each meaningful x, some started buildings remain relevant while others have expired.

reasoning: If the recorded height is not the maximum active height, the visible contour is wrong even if every building was visited.

answer: Invariant: after processing a relevant x, current skyline height equals the maximum height among buildings active at x.

next: What evidence is active in one frame?

After x=7, active height 12 is the maximum, so the prior visible height 15 must transition to 12.

Scope

question: What evidence is active in one Skyline frame?

evidence: The current frame has one x-coordinate and the buildings already active at that x.

reasoning: Expired buildings cannot affect the visible height. Future starts are not active evidence; they only help determine where a later frame begins.

answer: Scope: current x-coordinate plus currently active-building state, represented by height and right boundary.

next: What evidence determines the next meaningful frame?

At x=5, active buildings end at 7, 9, and 12. The next unconsumed left boundary is separate Next-Scope evidence, not part of Scope.

Scanner

question: Which coordinates are lawful Scanner destinations?

evidence: Next-Scope evidence is the next unconsumed building left boundary and the right boundary of the current tallest active building.

reasoning: Visibility changes only at those boundaries. Scanning every integer x repeats unchanged Scope, so the Scanner derives the next x from the two candidates.

answer: Scanner: Direct Sweep from one meaningful x to the next; no materialized 2n event list is required.

next: How does the current Scope become the next Scope?

From x=5, the next left is absent and tallest right is 7; advance directly to x=7 rather than testing x=6.

Next Scope

question: How does old Scope become next Scope?

evidence: At x=3, height 15 is active. The Scanner uses its right boundary 7 as Next-Scope evidence; height 12 remains active after that move.

reasoning: Scope defines active evidence now. Next-Scope evidence selects the next meaningful x. The Scanner then removes expired entries and adds starts to transform state.

answer: Move to the derived x, add all buildings starting there, then discard entries whose right boundary is at or before x.

next: Which active evidence must survive that movement?

Old Scope x=3 contains active heights 15 and 10. At next x=7, height 15 expires and the retained height 12 becomes visible.

Persistence Requirement

question: When Scope changes, what required information would be lost?

evidence: A building can start at x=5 but remain relevant after later Scanner frames at x=7 and x=9.

reasoning: The next frame cannot reconstruct earlier starts from its local coordinate alone. The invariant still needs their height and expiration boundary.

answer: Persistence Requirement: retain every started building until its right boundary is no longer beyond the current x.

next: Which structure supplies both persistence and repeated maximum queries?

The building [5,12,12] starts at x=5, survives x=7 and x=9, then expires at x=12.

Recorder

question: What smallest Recorder preserves that evidence?

evidence: Each frame needs insert by start, removal of expired top entries, and access to maximum active height.

reasoning: A max-priority queue keeps active candidates ordered by height. Lazy removal is lawful because an expired non-top candidate cannot affect the current maximum.

answer: Recorder: max heap / PriorityQueue of (right, height), prioritized by descending height.

next: When should the Answer Space receive a new key point?

At x=3 enqueue (7,15). It becomes the heap top; at x=7 lazy removal exposes the retained (12,12) entry.

Finalizer

question: What completes the constructed skyline?

evidence: The required output contains only changes in the visible maximum and ends after every active building expires.

reasoning: Compare the heap maximum with the last emitted height after each frame. Equality means no new contour point; a difference is the exact required transition.

answer: Finalizer: append [x, currentHeight] exactly when maximum active height differs from the prior emitted height.

next: What direct transcription is correct before the state-reduction audit?

At x=12 the heap becomes empty, current height becomes zero, and [12,0] is appended to close the skyline.

First Lawful Route

Baseline: Baseline route: create 2n boundary events, sort them, sweep left to right, and use a max heap.

Materializing two events per building, sorting by x, and maintaining active heights visits all relevant state changes without scanning coordinates.

The baseline event list has starts (2,10),(3,15),(5,12) and ends (7,15),(9,10),(12,12).

State-Reduction Audit

Rule: For every piece of state, ask whether the Invariant actually requires it to persist.

For every piece of state, ask whether the Invariant actually requires it to persist. The event list does not: both candidates for next x are already derivable.

Remove: explicit 2n event list. Keep: active-building Recorder.

REMOVE: 2n event list. KEEP: active-building heap. After left boundary 5, tallest right 7 supplies the next x without an event object.

Direct Sweep Derivation

The active-max-height invariant is unchanged; only the source of the next x is reduced.

Optimized Scope: input index, current x, and active max heap. Optimized Scanner: Direct Sweep over dynamically derived boundary coordinates.

Candidate next x values are 3 then 5 from input, then 7 from heap top; the sequence matches the event route without event allocation.

Optimized Recorder + Complexity

A priority queue stores precisely active buildings and makes maximum access immediate. Each item enters once and is removed once.

Recorder: Final Recorder: a max-priority queue keyed by height, storing right boundaries for lazy expiry.

O(n log n) time: O(n) heap insertions and removals with no event-list sorting pass. O(n) auxiliary space for active buildings in the priority queue.

The 12-Question Skyline Map

#QuestionDerived answer
1-3What must be returned?Ordered [x,height] transitions
4-6What evidence changes visibility?Active-building maximum at boundaries
7What is the Intent?Construction / Assembly
8What must remain true?Current height is maximum active height
9-10What is Scope and Scanner?Direct Sweep through meaningful x values
11What persists?Started, unexpired buildings
12What follows?Max heap Recorder; emit changed height

CCF-Compliant Python 3 Solution

from heapq import heappop, heappush

class Solution:
    def getSkyline(self, buildings):
        # Answer Space: ordered [x, height] key points.
        result = []
        # Problem Space: input is ordered by building left boundary.
        active = []
        index = 0
        previous = 0
        while index < len(buildings) or active:
            # Scope: next input start and active buildings.
            next_left = buildings[index][0] if index < len(buildings) else float('inf')
            next_right = active[0][1] if active else float('inf')
            # Scanner: Direct Sweep chooses the next meaningful x.
            x = min(next_left, next_right)
            while index < len(buildings) and buildings[index][0] == x:
                left, right, height = buildings[index]
                # Persistence Requirement: retain started buildings until expiry.
                heappush(active, (-height, right))
                index += 1
            while active and active[0][1] <= x:
                heappop(active)
            # Invariant: this is the maximum active height at x.
            height = -active[0][0] if active else 0
            if height != previous:
                # Finalizer: only visible height transitions enter the answer.
                result.append([x, height])
                previous = height
            # Recorder: heapq keeps active buildings ordered by negative height.
            # Efficiency Proof: next x is derived; no 2n event list is materialized.
        return result

Complexity Analysis

Time: O(n log n) time: O(n) heap insertions and removals with no event-list sorting pass.

Auxiliary space: O(n) auxiliary space for active buildings in the priority queue.

Lazy cleanup can pop several expired entries in one frame, but every entry is popped once overall, so the total cleanup is O(n log n).

Why This Is Better Than Pattern Guessing

Pattern-first says: “This is Skyline. Use Sweep Line plus Heap.” CCF asks what changes the answer, what stays true, how Scope moves, and what must survive that movement. The Scanner and Recorder then emerge as consequences of evidence.

Algorithm names are conclusions, not starting assumptions.