A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Given the locations and heights of all the buildings, return the skyline formed by these buildings collectively.
The geometric information of each building is given in the array buildings
where buildings[i] = [lefti, righti, heighti]
:
lefti
is the x coordinate of the left edge of the ith
building.righti
is the x coordinate of the right edge of the ith
building.heighti
is the height of the ith
building.You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0
.
The skyline should be represented as a list of "key points" sorted by their x-coordinate in the form [[x1,y1],[x2,y2],...]
. Each key point is the left endpoint of some horizontal segment in the skyline except the last point in the list, which always has a y-coordinate 0
and is used to mark the skyline's termination where the rightmost building ends. Any ground between the leftmost and rightmost buildings should be part of the skyline's contour.
Note: There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...,[2 3],[4 5],[7 5],[11 5],[12 7],...]
is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...,[2 3],[4 5],[12 7],...]
Example 1:
Input: buildings = [[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]] Explanation: Figure A shows the buildings of the input. Figure B shows the skyline formed by those buildings. The red points in figure B represent the key points in the output list.
Example 2:
Input: buildings = [[0,2,3],[2,5,3]] Output: [[0,3],[5,0]]
Constraints:
1 <= buildings.length <= 104
0 <= lefti < righti <= 231 - 1
1 <= heighti <= 231 - 1
buildings
is sorted by lefti
in non-decreasing order.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The Skyline Problem asks us to draw the outline of a city given the shapes of its buildings. A brute-force approach imagines dividing the city's area into tiny vertical slices and checks each slice to see which buildings are present.
Here's how the algorithm would work step-by-step:
def the_skyline_problem_brute_force(buildings): if not buildings: return [] minimum_left = min(building[0] for building in buildings) maximum_right = max(building[1] for building in buildings) # Divide the area into strips skyline = [] for strip_position in range(minimum_left, maximum_right + 1): max_height_for_strip = 0 # Find the tallest building in the current strip for left_position, right_position, building_height in buildings: if left_position <= strip_position < right_position: max_height_for_strip = max(max_height_for_strip, building_height) if not skyline or skyline[-1][1] != max_height_for_strip: skyline.append([strip_position, max_height_for_strip]) # Remove unnecessary vertical lines final_skyline = [] for i in range(len(skyline)): if i == 0 or skyline[i][1] != skyline[i-1][1]: final_skyline.append(skyline[i]) return final_skyline
The efficient strategy involves processing building information in an ordered manner and keeping track of the highest buildings at each point. We simulate a 'sweep line' moving across the cityscape, updating the skyline as it encounters the start and end of buildings.
Here's how the algorithm would work step-by-step:
import heapq
def get_skyline(buildings):
skyline = []
events = []
# Convert buildings to events (start and end)
for left, right, height in buildings:
events.append((left, -height, right)) # Start event: height is negative
events.append((right, 0, 0)) # End event: height is 0
# Sort events by x coordinate, then by height (start events first)
events.sort()
live_buildings = [] # Priority queue to store current building heights
current_max_height = 0
for event_x, event_height, event_right in events:
# Start of building
if event_height < 0:
heapq.heappush(live_buildings, (event_height, event_right))
# End of building
else:
# Filtering out buildings that have already ended.
while live_buildings and live_buildings[0][1] <= event_x:
heapq.heappop(live_buildings)
# Get current max height
if live_buildings:
new_max_height = -live_buildings[0][0]
else:
new_max_height = 0
# If skyline height changes, add to result.
if current_max_height != new_max_height:
skyline.append([event_x, new_max_height])
current_max_height = new_max_height
return skyline
Case | How to Handle |
---|---|
Empty buildings array | Return an empty skyline list immediately as there are no buildings to process. |
Buildings with the same start and end x-coordinates | Merge buildings with identical x coordinates by selecting the maximum height among them. |
Buildings with zero height | Ignore these buildings as they do not contribute to the skyline. |
Buildings with negative coordinates or heights | Handle negative coordinates by translating all buildings to positive space, or throw an error, and throw an error for negative heights as building heights cannot be negative. |
Large number of buildings with small coordinate ranges | Ensure that the algorithm's space complexity is optimized to avoid excessive memory usage when processing numerous buildings within a confined area. |
Integer overflow when calculating height differences or merging ranges | Use appropriate data types (e.g., long) to prevent integer overflow during calculations. |
Overlapping buildings with same start and end x-coordinates, but different heights | Choose the largest height to represent the skyline at that x coordinate. |
Very large x-coordinate values | Ensure that the data structures used to represent the skyline (e.g., arrays, trees) can accommodate these large values without performance degradation or memory issues. |