Sorting ensures that events are processed in left-to-right order. If two events occur at the same x-coordinate, “start” events are processed before “end” events to handle overlaps correctly and prioritize taller buildings.
What is the Skyline Problem in Python?
Key takeaways:
The Skyline Problem involves drawing the outline of buildings on a 2D plane based on their positions and heights. The goal is to find the shape of the skyline when viewed from a distance.
The Skyline Problem has applications in computer graphics and urban planning.
It combines sorting, heap-based data structures, and event-driven computation, showcasing algorithm design and optimization.
This problem is commonly asked in technical interviews as it tests problem-solving skills, understanding of data structures, and algorithmic efficiency.
What is the Skyline Problem?
The Skyline Problem is a popular computer science interview question. It involves drawing an outline of several buildings in a 2D plane. This problem provides an excellent opportunity to explore and apply various programming concepts, such as sorting, divide and conquer, and data structures.
Problem statement
Imagine standing at a distance, viewing the skyline of a city. The skyline is the shape formed by all the buildings in the city when viewed together. Your task is to determine the shape of this skyline, given all the buildings’ position and height. Each building is represented by three values in the array buildings, where
is the –coordinate where the building starts. is the –coordinate where the building ends. is the height of the building.
All buildings are rectangles that sit on flat ground (height
Note: The output skyline should not have multiple horizontal lines at the same height in a row. For example, an output like
is incorrect. The three lines with height should be combined into one, so the correct version would be .
Examples
Solution to find the skyline
The Skyline Problem aims to represent the outline of a set of rectangular buildings when viewed from a distance. This is achieved by identifying key points where the height of the silhouette changes. To solve this problem efficiently, an optimized
Here are the detailed steps of the algorithm:
Start by creating a list of
for all buildings. For each buildingcritical points Critical points are the x-coordinates where the height of the skyline changes. These points represent significant events where a building either starts or ends, influencing the overall skyline shape. [left, right, height]:Add a “start” event
(left, -height)to indicate the start of the building.Add an “end” event
(right, height)to indicate the end of the building.The negative height in “start” events ensures taller buildings are processed first when sorting, preventing incorrect skyline calculations. It also ensures “start” events are handled before “end” events at the same x-coordinate, maintaining correct height transitions.
Sort the critical points by x-coordinate. If two points have the same x-coordinate, prioritize “start” events (negative height) over “end” events (positive height) to handle overlaps correctly.
Next, initialize the following data structures:
A
max_heapas an empty list to track active building heights.A dictionary
active_heightsto store counts of building heights, ensuring efficient pruning.When a building’s endpoint is reached, its height remains in the
max_heapuntil explicitly removed. To ensure themax_heapreflects only active buildings, we prune heights that no longer correspond to valid buildings.
A variable
prev_max_heightset to 0.An empty list
resultto store the key points of the skyline.
Process each critical point
(x, height)in the sorted list:If
height < 0(start event), add the building height toactive_heightsand push its negative tomax_heap.Otherwise, decrement
active_heightsor remove the corresponding height inactive_heightsifactive_heights = 0.While
max_heapis not empty, and the tallest building inmax_heapis no longer active (not inactive_heights), remove it from the max heap.Compute the current maximum height as
-max_heap[0]if the heap is not empty, or 0 if it is.If the current maximum height differs from
prev_max_height, record the change by appending[x, current_max_height]toresultand updateprev_max_height.
Return
resultas the list of key points defining the skyline.
Let’s look at the following illustration to get a better understanding of the solution:
Python code to find the skyline
Let’s look at the example code for the approach discussed above:
from heapq import heappush, heappopfrom collections import defaultdictdef get_skyline(buildings):# Create critical pointscritical_points = []for left, right, height in buildings:critical_points.append((left, -height)) # Start of a buildingcritical_points.append((right, height)) # End of a building# Sort critical pointscritical_points.sort(key=lambda x: (x[0], x[1]))# Data structuresmax_heap = []active_heights = defaultdict(int)prev_max_height = 0result = []# Process each critical pointfor x, height in critical_points:if height < 0: # Start of a buildingheappush(max_heap, height)active_heights[-height] += 1else: # End of a buildingactive_heights[height] -= 1if active_heights[height] == 0:del active_heights[height]# Clean up the heapwhile max_heap and -max_heap[0] not in active_heights:heappop(max_heap)# Determine the current max heightcurrent_max_height = -max_heap[0] if max_heap else 0# Check if the height has changedif current_max_height != prev_max_height:result.append([x, current_max_height])prev_max_height = current_max_heightreturn resultdef main():buildings = [[[1, 7, 9], [3, 8, 8], [5, 9, 7]],[[1, 5, 10]],[[1, 2, 3], [4, 5, 6], [7, 8, 9]],[[1, 2, 5], [2, 3, 8]],[[0, 2, 10], [1, 2, 10], [2, 3, 10]],[[1000, 2000, 50], [1500, 2500, 80], [3000, 4000, 60]]]for i, building in enumerate(buildings, 1):print(f"{i}. \tBuildings =", building)print("\tSkyline =", get_skyline(building))print("-" * 100)if __name__ == "__main__":main()
Complexity analysis
Time complexity:
The overall time complexity of the solution is
The algorithm sorts all critical points. Since there are
events for each building (start and end), the total number of events is , where is the number of buildings. Sorting takes
, which simplifies to .
Each event is processed once, and for each event:
Adding or removing an element in the max-heap takes
time. Cleaning the heap involves checking the top of the heap and removing inactive elements, which can also take
time per operation in the worst case. The total number of operations related to the heap is proportional to the number of events,
. Thus, the heap operations contribute , which simplifies to .
Space complexity:
The overall space complexity of the solution is
Storing
critical points requires , which simplifies to . The max-heap stores at most
active buildings, requiring space. The dictionary stores counts of building heights, also requiring
space in the worst case.
Additional resources
To learn more about data structures and algorithms and practice more LeetCode-based problems such as The Skyline Problem, look at the following list of curated courses at Educative:
If you wish to implement your solution in C++, then take a look at the “The Skyline Problem” available on the Grokking Coding Interview Patterns in C++ course.
Frequently asked questions
Haven’t found what you were looking for? Contact Us
Why are critical points sorted before processing?
What are some real-world applications of the skyline problem?
How does the algorithm handle buildings with the same height?
Can this problem be solved using Union-Find or other data structures?
Free Resources