Given n rectangular buildings in a 2-dimensional city, compute the skyline of these buildings, eliminating hidden lines. The goal is to view buildings from a distance and keep only the visible outer boundary. All buildings share a common base, and each building is represented by a triplet (left, right, ht), where:
'left': is the x coordinate of the left side (or wall).
'right': is x coordinate of right side.
'ht': is the height of the building.
The skyline is represented as a collection of key points (left, ht), where each point indicates that at x = left, the height of the skyline becomes ht.
One thing is obvious, we build the skyline from left to right. Now we know the left point [1, 11] of the first building is going to be in the output as it is the leftmost point.
If we take a look at the right point of the first building which is [5, 11], we find that this point cannot be considered because there is a higher height building (third building in our input array [3, 9, 13[). So we ignore it.
Now we see the second building [2, 7, 6], its both left and right are covered. Left is covered by first building and right is covered by third building because its height is smaller than both of its neighbors. So we ignore its both left and right points.
We process the remaining buildings in the same way, only adding points where the height changes, and ignoring parts that are hidden by taller buildings.
[Naive Approach] Sweep Line Algorithm - O(n^2) Time and O(n) Space
Get all corner x coordinates of all buildings in an array say points[]. We are mainly going to have 2n points in this array as we have left and right for every building.
Sort the points[] to simulate the sweep line from left to right.
Now traverse through the sorted point[] and for every x point check which building has the maximum height at this point and add the maximum height to the skyline if the maximum height is different from the previously added height to the skyline.
C++
#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;vector<pair<int,int>>getSkyline(vector<vector<int>>&arr){vector<int>points;// Collect all left and right x-coordinates// of buildingsfor(auto&b:arr){points.push_back(b[0]);points.push_back(b[1]);}// Sort all critical pointssort(points.begin(),points.end());vector<pair<int,int>>res;intprev=0;// Traverse through each point to// determine skyline heightfor(intx:points){intmaxH=0;// Check which buildings cover the // current x and get the max heightfor(auto&b:arr){intl=b[0],r=b[1],h=b[2];if(l<=x&&x<r){maxH=max(maxH,h);}}// Add to result if height has changedif(res.empty()||maxH!=prev){res.push_back({x,maxH});prev=maxH;}}returnres;}intmain(){vector<vector<int>>arr={{2,9,10},{3,6,15},{5,12,12}};vector<pair<int,int>>skyline=getSkyline(arr);for(auto&p:skyline){cout<<"("<<p.first<<", "<<p.second<<") ";}cout<<endl;return0;}
Java
importjava.util.*;classSolution{publicArrayList<int[]>getSkyline(int[][]arr){ArrayList<Integer>points=newArrayList<>();// Collect all left and right x-coordinates// of buildingsfor(int[]b:arr){points.add(b[0]);points.add(b[1]);}// Sort all critical pointsCollections.sort(points);ArrayList<int[]>res=newArrayList<>();intprev=0;// Traverse through each point to// determine skyline heightfor(intx:points){intmaxH=0;// Check which buildings cover the // current x and get the max heightfor(int[]b:arr){intl=b[0],r=b[1],h=b[2];if(l<=x&&x<r){maxH=Math.max(maxH,h);}}// Add to result if height has changedif(res.isEmpty()||maxH!=prev){res.add(newint[]{x,maxH});prev=maxH;}}returnres;}publicstaticvoidmain(String[]args){int[][]arr={{2,9,10},{3,6,15},{5,12,12}};Solutionsol=newSolution();ArrayList<int[]>skyline=sol.getSkyline(arr);for(int[]p:skyline){System.out.print("("+p[0]+", "+p[1]+") ");}System.out.println();}}
Python
defgetSkyline(arr):points=[]# Collect all left and right x-coordinates# of buildingsforbinarr:points.append(b[0])points.append(b[1])# Sort all critical pointspoints.sort()res=[]prev=0# Traverse through each point to# determine skyline heightforxinpoints:maxH=0# Check which buildings cover the # current x and get the max heightforbinarr:l,r,h=bifl<=x<r:maxH=max(maxH,h)# Add to result if height has changedifnotresormaxH!=prev:res.append((x,maxH))prev=maxHreturnresif__name__=='__main__':arr=[[2,9,10],[3,6,15],[5,12,12]]skyline=getSkyline(arr)forpinskyline:print(f'({p[0]}, {p[1]}) ',end='')print()
C#
usingSystem;usingSystem.Collections.Generic;classGfG{staticList<int[]>getSkyline(int[][]arr){List<int>points=newList<int>();// Collect all left and right x-coordinates// of buildingsforeach(varbinarr){points.Add(b[0]);points.Add(b[1]);}// Sort all critical pointspoints.Sort();List<int[]>res=newList<int[]>();intprev=0;// Traverse through each point to// determine skyline heightforeach(varxinpoints){intmaxH=0;// Check which buildings cover the // current x and get the max heightforeach(varbinarr){intl=b[0],r=b[1],h=b[2];if(l<=x&&x<r){maxH=Math.Max(maxH,h);}}// Add to result if height has changedif(res.Count==0||maxH!=prev){res.Add(newint[]{x,maxH});prev=maxH;}}returnres;}publicstaticvoidMain(string[]args){int[][]arr={newint[]{2,9,10},newint[]{3,6,15},newint[]{5,12,12}};List<int[]>skyline=getSkyline(arr);foreach(varpinskyline){Console.Write("("+p[0]+", "+p[1]+") ");}Console.WriteLine();}}
JavaScript
functiongetSkyline(arr){constpoints=[];// Collect all left and right x-coordinates// of buildingsfor(constbofarr){points.push(b[0]);points.push(b[1]);}// Sort all critical pointspoints.sort((a,b)=>a-b);constres=[];letprev=0;// Traverse through each point to// determine skyline heightfor(constxofpoints){letmaxH=0;// Check which buildings cover the // current x and get the max heightfor(constbofarr){const[l,r,h]=b;if(l<=x&&x<r){maxH=Math.max(maxH,h);}}// Add to result if height has changedif(res.length===0||maxH!==prev){res.push([x,maxH]);prev=maxH;}}returnres;}constarr=[[2,9,10],[3,6,15],[5,12,12]];constskyline=getSkyline(arr);for(constpofskyline){process.stdout.write(`(${p[0]}, ${p[1]}) `);}console.log();
Output
(2, 10) (3, 15) (6, 12) (12, 0)
[Expected Approach] Sweep Line and Priority Queue - O(n Log n) Time and O(n) Space
The approach uses a sweep line algorithm with a priority queue (max-heap) to quickly get the current maximum heights as the sweep line moves from left to right. The idea is to again store all points, but this time we also store building indexes. We use priority queue to have all building point in it when we reach the next points, so that we can quickly find the maximum.
Step-by-step explanation:
Prepare edges by storing both start and end of each building as (x-coordinate, building index).
Sort all edges by x-coordinate to process them from left to right.
Maintain a priority queue (max-heap) of active buildings storing (height, end).
Add buildings to the priority queue when a start edge is encountered.
Remove buildings whose end is less than or equal to the current x-coordinate.
The top of the priority queue gives the current maximum height.
If this height changes from the previous value, add (x, height) to the skyline.
Return the collected points as the final skyline.
C++
#include<iostream>#include<vector>#include<queue>#include<algorithm>usingnamespacestd;vector<vector<int>>getSkyline(vector<vector<int>>&arr){vector<pair<int,int>>e;priority_queue<pair<int,int>>pq;vector<vector<int>>skyline;// Store both start and end edgesfor(inti=0;i<arr.size();++i){e.push_back({arr[i][0],i});e.push_back({arr[i][1],i});}// Sort edges by x-coordinate (sweep line)sort(e.begin(),e.end());inti=0;while(i<e.size()){intcurr_x=e[i].first;// Process all edges at same xwhile(i<e.size()&&e[i].first==curr_x){intidx=e[i].second;// If it's a start edge, add building to heapif(arr[idx][0]==curr_x)pq.emplace(arr[idx][2],arr[idx][1]);++i;}// Remove buildings that are no longer active// (their right boundary is passed)while(!pq.empty()&&pq.top().second<=curr_x)pq.pop();// Current max height among active buildingsintcurr_height=pq.empty()?0:pq.top().first;// Add point only if height changesif(skyline.empty()||skyline.back()[1]!=curr_height)skyline.push_back({curr_x,curr_height});}returnskyline;}intmain(){vector<vector<int>>arr={{1,5,11},{2,7,6},{3,9,13},{12,16,7},{14,25,3},{19,22,18},{23,29,13},{24,28,4}};vector<vector<int>>result=getSkyline(arr);for(constauto&p:result){cout<<"["<<p[0]<<", "<<p[1]<<"] ";}cout<<endl;return0;}
Java
importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Collections;importjava.util.PriorityQueue;publicclassGfG{publicstaticArrayList<ArrayList<Integer>>getSkyline(int[][]arr){intidx=0;ArrayList<int[]>e=newArrayList<>();PriorityQueue<int[]>pq=newPriorityQueue<>((a,b)->b[0]-a[0]);ArrayList<ArrayList<Integer>>skyline=newArrayList<>();// Store both start and end edgesfor(inti=0;i<arr.length;i++){e.add(newint[]{arr[i][0],i});e.add(newint[]{arr[i][1],i});}// Sort edges by x-coordinate (sweep line)Collections.sort(e,(a,b)->Integer.compare(a[0],b[0]));while(idx<e.size()){intcurr_x=e.get(idx)[0];// Process all edges at same xwhile(idx<e.size()&&curr_x==e.get(idx)[0]){intbuilding_idx=e.get(idx)[1];// If it's a start edge, add building to heapif(arr[building_idx][0]==curr_x)pq.add(newint[]{arr[building_idx][2],arr[building_idx][1]});idx++;}// Remove buildings that are no longer active// (their right boundary is passed)while(!pq.isEmpty()&&pq.peek()[1]<=curr_x)pq.poll();// Current max height among active buildingsintcurr_height=pq.isEmpty()?0:pq.peek()[0];// Add point only if height changesif(skyline.isEmpty()||skyline.get(skyline.size()-1).get(1)!=curr_height){skyline.add(newArrayList<>(Arrays.asList(curr_x,curr_height)));}}returnskyline;}publicstaticvoidmain(String[]args){int[][]arr={{1,5,11},{2,7,6},{3,9,13},{12,16,7},{14,25,3},{19,22,18},{23,29,13},{24,28,4}};ArrayList<ArrayList<Integer>>result=getSkyline(arr);for(ArrayList<Integer>p:result){System.out.print("["+p.get(0)+", "+p.get(1)+"] ");}System.out.println();}}
Python
importheapqdefgetSkyline(arr):idx=0e=[]pq=[]skyline=[]# Store both start and end edgesforiinrange(len(arr)):e.append((arr[i][0],i))e.append((arr[i][1],i))# Sort edges by x-coordinate (sweep line)e.sort()whileidx<len(e):curr_x=e[idx][0]# Process all edges at same xwhileidx<len(e)andcurr_x==e[idx][0]:building_idx=e[idx][1]# If it's a start edge, add building to heap# (use negative height to simulate max-heap)ifarr[building_idx][0]==curr_x:heapq.heappush(pq,(-arr[building_idx][2],arr[building_idx][1]))idx+=1# Remove buildings that are no longer active# (their right boundary is passed)whilepqandpq[0][1]<=curr_x:heapq.heappop(pq)# Current max height among active buildingscurr_height=0ifnotpqelse-pq[0][0]# Add point only if height changesifnotskylineorskyline[-1][1]!=curr_height:skyline.append([curr_x,curr_height])returnskylineif__name__=="__main__":arr=[[1,5,11],[2,7,6],[3,9,13],[12,16,7],[14,25,3],[19,22,18],[23,29,13],[24,28,4]]result=getSkyline(arr)forpinresult:print(f"[{p[0]}, {p[1]}]",end=" ")print()
C#
usingSystem;usingSystem.Collections.Generic;classGfG{staticList<List<int>>getSkyline(int[,]arr){intidx=0;List<int[]>e=newList<int[]>();// Max-heap based on height (store: height, end)SortedSet<int[]>pq=newSortedSet<int[]>(Comparer<int[]>.Create((a,b)=>a[0]!=b[0]?b[0].CompareTo(a[0]):b[1].CompareTo(a[1])));List<List<int>>skyline=newList<List<int>>();// Store both start and end edgesfor(inti=0;i<arr.GetLength(0);i++){e.Add(newint[]{arr[i,0],i});e.Add(newint[]{arr[i,1],i});}// Sort edges by x-coordinate (sweep line)e.Sort((a,b)=>a[0].CompareTo(b[0]));while(idx<e.Count){intcurr_x=e[idx][0];// Process all edges at same xwhile(idx<e.Count&&curr_x==e[idx][0]){intbuilding_idx=e[idx][1];// If it's a start edge, add building to heapif(arr[building_idx,0]==curr_x)pq.Add(newint[]{arr[building_idx,2],arr[building_idx,1]});idx++;}// Remove buildings that are no longer active// (their right boundary is passed)while(pq.Count>0&&pq.Min[1]<=curr_x)pq.Remove(pq.Min);// Current max height among active buildingsintcurr_height=pq.Count==0?0:pq.Min[0];// Add point only if height changesif(skyline.Count==0||skyline[skyline.Count-1][1]!=curr_height)skyline.Add(newList<int>{curr_x,curr_height});}returnskyline;}staticvoidMain(string[]args){int[,]arr={{1,5,11},{2,7,6},{3,9,13},{12,16,7},{14,25,3},{19,22,18},{23,29,13},{24,28,4}};List<List<int>>result=getSkyline(arr);foreach(varpinresult){Console.Write($"[{p[0]}, {p[1]}] ");}Console.WriteLine();}}
JavaScript
functiongetSkyline(arr){letidx=0;lete=[];letpq=[];letskyline=[];// Store both start and end edgesfor(leti=0;i<arr.length;i++){e.push([arr[i][0],i]);e.push([arr[i][1],i]);}// Sort edges by x-coordinate (sweep line)e.sort((a,b)=>a[0]-b[0]);while(idx<e.length){letcurr_x=e[idx][0];// Process all edges at same xwhile(idx<e.length&&curr_x===e[idx][0]){letbuilding_idx=e[idx][1];// If it's a start edge, add building to active listif(arr[building_idx][0]===curr_x){pq.push([arr[building_idx][2],arr[building_idx][1]]);}idx++;}// Remove buildings that are no longer active// (their right boundary is passed)pq=pq.filter(item=>item[1]>curr_x);// Get current max height among active buildingsletcurr_height=0;for(leti=0;i<pq.length;i++){curr_height=Math.max(curr_height,pq[i][0]);}// Add point only if height changesif(skyline.length===0||skyline[skyline.length-1][1]!==curr_height){skyline.push([curr_x,curr_height]);}}returnskyline;}// Driver Codeletarr=[[1,5,11],[2,7,6],[3,9,13],[12,16,7],[14,25,3],[19,22,18],[23,29,13],[24,28,4]];letresult=getSkyline(arr);result.forEach(p=>console.log(`[${p[0]}, ${p[1]}]`));