Given n rectangular buildings in a 2-dimensional city, computes the skyline of these buildings, eliminating hidden lines. The main task is to view buildings from a side and remove all sections that are not visible. All buildings share common bottom and every building is represented by triplet (left, ht, right)
- ‘left’: is x coordinated of left side (or wall).
- ‘right’: is x coordinate of right side
- ‘ht’: is height of building.
A skyline is a collection of rectangular strips. A rectangular strip is represented as a pair (left, ht) where left is x coordinate of left side of strip and ht is height of strip. Examples:
Input: Array of buildings
{ (1, 11, 5), (2, 6, 7), (3, 13, 9), (12, 7, 16), (14, 3, 25),
(19, 18, 22), (23, 13, 29), (24, 4, 28) }
Output: Skyline (an array of rectangular strips)
A strip has x coordinate of left side and height
(1, 11), (3, 13), (9, 0), (12, 7), (16, 3), (19, 18),
(22, 3), (25, 0)
Below image is for input 1 :
Consider following as another example when there is only one
building
Input: {(1, 11, 5)}
Output: (1, 11), (5, 0)
A Simple Solution is to initialize skyline or result as empty, then one by one add buildings to skyline. A building is added by first finding the overlapping strip(s). If there are no overlapping strips, the new building adds new strip(s). If overlapping strip is found, then height of the existing strip may increase. Time complexity of this solution is O(n2) We can find Skyline in Θ(nLogn) time using Divide and Conquer. The idea is similar to Merge Sort, divide the given set of buildings in two subsets. Recursively construct skyline for two halves and finally merge the two skylines. How to Merge two Skylines? The idea is similar to merge of merge sort, start from first strips of two skylines, compare x coordinates. Pick the strip with smaller x coordinate and add it to result. The height of added strip is considered as maximum of current heights from skyline1 and skyline2. Example to show working of merge:
Height of new Strip is always obtained by takin maximum of following
(a) Current height from skyline1, say 'h1'.
(b) Current height from skyline2, say 'h2'
h1 and h2 are initialized as 0. h1 is updated when a strip from
SkyLine1 is added to result and h2 is updated when a strip from
SkyLine2 is added.
Skyline1 = {(1, 11), (3, 13), (9, 0), (12, 7), (16, 0)}
Skyline2 = {(14, 3), (19, 18), (22, 3), (23, 13), (29, 0)}
Result = {}
h1 = 0, h2 = 0
Compare (1, 11) and (14, 3). Since first strip has smaller left x,
add it to result and increment index for Skyline1.
h1 = 11, New Height = max(11, 0)
Result = {(1, 11)}
Compare (3, 13) and (14, 3). Since first strip has smaller left x,
add it to result and increment index for Skyline1
h1 = 13, New Height = max(13, 0)
Result = {(1, 11), (3, 13)}
Similarly (9, 0) and (12, 7) are added.
h1 = 7, New Height = max(7, 0) = 7
Result = {(1, 11), (3, 13), (9, 0), (12, 7)}
Compare (16, 0) and (14, 3). Since second strip has smaller left x,
it is added to result.
h2 = 3, New Height = max(7, 3) = 7
Result = {(1, 11), (3, 13), (9, 0), (12, 7), (14, 7)}
Compare (16, 0) and (19, 18). Since first strip has smaller left x,
it is added to result.
h1 = 0, New Height = max(0, 3) = 3
Result = {(1, 11), (3, 13), (9, 0), (12, 7), (14, 7), (16, 3)}
Since Skyline1 has no more items, all remaining items of Skyline2
are added
Result = {(1, 11), (3, 13), (9, 0), (12, 7), (14, 7), (16, 3),
(19, 18), (22, 3), (23, 13), (29, 0)}
One observation about above output is, the strip (14, 7) is redundant
(There is already an strip of same height). We remove all redundant
strips.
Result = {(1, 11), (3, 13), (9, 0), (12, 7), (16, 3), (19, 18),
(22, 3), (23, 13), (29, 0)}
In below code, redundancy is handled by not appending a strip if the
previous strip in result has same height.
Below is C++ implementation of above idea.
C++
#include <iostream>
using namespace std;
struct Building {
int left;
int ht;
int right;
};
class Strip {
int left;
int ht;
public :
Strip( int l = 0, int h = 0)
{
left = l;
ht = h;
}
friend class SkyLine;
};
class SkyLine {
Strip* arr;
int capacity;
int n;
public :
~SkyLine() { delete [] arr; }
int count() { return n; }
SkyLine* Merge(SkyLine* other);
SkyLine( int cap)
{
capacity = cap;
arr = new Strip[cap];
n = 0;
}
void append(Strip* st)
{
if (n > 0 && arr[n - 1].ht == st->ht)
return ;
if (n > 0 && arr[n - 1].left == st->left) {
arr[n - 1].ht = max(arr[n - 1].ht, st->ht);
return ;
}
arr[n] = *st;
n++;
}
void print()
{
for ( int i = 0; i < n; i++) {
cout << " (" << arr[i].left << ", "
<< arr[i].ht << "), " ;
}
}
};
SkyLine* findSkyline(Building arr[], int l, int h)
{
if (l == h) {
SkyLine* res = new SkyLine(2);
res->append(
new Strip(
arr[l].left, arr[l].ht));
res->append(
new Strip(
arr[l].right, 0));
return res;
}
int mid = (l + h) / 2;
SkyLine* sl = findSkyline(
arr, l, mid);
SkyLine* sr = findSkyline(
arr, mid + 1, h);
SkyLine* res = sl->Merge(sr);
delete sl;
delete sr;
return res;
}
SkyLine* SkyLine::Merge(SkyLine* other)
{
SkyLine* res = new SkyLine(
this ->n + other->n);
int h1 = 0, h2 = 0;
int i = 0, j = 0;
while (i < this ->n && j < other->n) {
if ( this ->arr[i].left < other->arr[j].left) {
int x1 = this ->arr[i].left;
h1 = this ->arr[i].ht;
int maxh = max(h1, h2);
res->append( new Strip(x1, maxh));
i++;
}
else {
int x2 = other->arr[j].left;
h2 = other->arr[j].ht;
int maxh = max(h1, h2);
res->append( new Strip(x2, maxh));
j++;
}
}
while (i < this ->n) {
res->append(&arr[i]);
i++;
}
while (j < other->n) {
res->append(&other->arr[j]);
j++;
}
return res;
}
int main()
{
Building arr[] = {
{ 1, 11, 5 }, { 2, 6, 7 }, { 3, 13, 9 }, { 12, 7, 16 }, { 14, 3, 25 }, { 19, 18, 22 }, { 23, 13, 29 }, { 24, 4, 28 }
};
int n = sizeof (arr) / sizeof (arr[0]);
SkyLine* ptr = findSkyline(arr, 0, n - 1);
cout << " Skyline for given buildings is \n" ;
ptr->print();
return 0;
}
|
Java
import java.util.*;
class Building {
int left, ht, right;
public Building( int left, int ht, int right) {
this .left = left;
this .ht = ht;
this .right = right;
}
}
class Strip {
int left, ht;
public Strip( int left, int ht) {
this .left = left;
this .ht = ht;
}
}
class SkyLine {
List<Strip> arr;
int capacity, n;
public SkyLine( int cap) {
this .arr = new ArrayList<>();
this .capacity = cap;
this .n = 0 ;
}
public int count() {
return this .n;
}
public SkyLine merge(SkyLine other) {
SkyLine res = new SkyLine( this .n + other.n);
int h1 = 0 , h2 = 0 , i = 0 , j = 0 ;
while (i < this .n && j < other.n) {
if ( this .arr.get(i).left < other.arr.get(j).left) {
int x1 = this .arr.get(i).left;
h1 = this .arr.get(i).ht;
int maxh = Math.max(h1, h2);
res.append( new Strip(x1, maxh));
i++;
} else {
int x2 = other.arr.get(j).left;
h2 = other.arr.get(j).ht;
int maxh = Math.max(h1, h2);
res.append( new Strip(x2, maxh));
j++;
}
}
while (i < this .n) {
res.append( this .arr.get(i));
i++;
}
while (j < other.n) {
res.append(other.arr.get(j));
j++;
}
return res;
}
public void append(Strip st) {
if ( this .n > 0 && this .arr.get( this .n- 1 ).ht == st.ht) {
return ;
}
if ( this .n > 0 && this .arr.get( this .n- 1 ).left == st.left) {
this .arr.get( this .n- 1 ).ht = Math.max( this .arr.get( this .n- 1 ).ht, st.ht);
return ;
}
this .arr.add(st);
this .n++;
}
public void printSkyline() {
System.out.println( "Skyline for given buildings is:" );
for ( int i = 0 ; i < this .n; i++) {
System.out.print( "(" + this .arr.get(i).left + ", " + this .arr.get(i).ht + "), " );
}
System.out.println();
}
}
class SkylineProblem {
public static SkyLine findSkyline(Building[] arr, int l, int h) {
if (l == h) {
SkyLine res = new SkyLine( 2 );
res.append( new Strip(arr[l].left, arr[l].ht));
res.append( new Strip(arr[l].right, 0 ));
return res;
}
int mid = (l + h) / 2 ;
SkyLine sl = findSkyline(arr, l, mid);
SkyLine sr = findSkyline(arr, mid+ 1 , h);
SkyLine res = sl.merge(sr);
return res;
}
public static void main(String[] args) {
Building[] arr = { new Building( 1 , 11 , 5 ), new Building( 2 , 6 , 7 ), new Building( 3 , 13 , 9 ),
new Building( 12 , 7 , 16 ), new Building( 14 , 3 , 25 ), new Building( 19 , 18 , 22 ),
new Building( 23 , 13 , 29 ), new Building( 24 , 4 , 28 )};
SkyLine res = findSkyline(arr, 0 , arr.length- 1 );
res.printSkyline();
}
}
|
Python3
class Building:
def __init__( self , left, ht, right):
self .left = left
self .ht = ht
self .right = right
class Strip:
def __init__( self , left = 0 , ht = 0 ):
self .left = left
self .ht = ht
class SkyLine:
def __init__( self , cap):
self .arr = []
self .capacity = cap
self .n = 0
def count( self ):
return self .n
def merge( self , other):
res = SkyLine( self .n + other.n)
h1, h2, i, j = 0 , 0 , 0 , 0
while i < self .n and j < other.n:
if self .arr[i].left < other.arr[j].left:
x1, h1 = self .arr[i].left, self .arr[i].ht
maxh = max (h1, h2)
res.append(Strip(x1, maxh))
i + = 1
else :
x2, h2 = other.arr[j].left, other.arr[j].ht
maxh = max (h1, h2)
res.append(Strip(x2, maxh))
j + = 1
while i < self .n:
res.append( self .arr[i])
i + = 1
while j < other.n:
res.append(other.arr[j])
j + = 1
return res
def append( self , st):
if self .n > 0 and self .arr[ self .n - 1 ].ht = = st.ht:
return
if self .n > 0 and self .arr[ self .n - 1 ].left = = st.left:
self .arr[ self .n - 1 ].ht = max ( self .arr[ self .n - 1 ].ht, st.ht)
return
self .arr.append(st)
self .n + = 1
def print_skyline( self ):
print ( "Skyline for given buildings is" )
for i in range ( self .n):
print ( " ({}, {})," . format ( self .arr[i].left, self .arr[i].ht), end = "")
print ()
def find_skyline(arr, l, h):
if l = = h:
res = SkyLine( 2 )
res.append(Strip(arr[l].left, arr[l].ht))
res.append(Strip(arr[l].right, 0 ))
return res
mid = (l + h) / / 2
sl = find_skyline(arr, l, mid)
sr = find_skyline(arr, mid + 1 , h)
res = sl.merge(sr)
return res
arr = [Building( 1 , 11 , 5 ), Building( 2 , 6 , 7 ), Building( 3 , 13 , 9 ), Building( 12 , 7 , 16 ), Building( 14 , 3 , 25 ), Building( 19 , 18 , 22 ), Building( 23 , 13 , 29 ), Building( 24 , 4 , 28 )]
n = len (arr)
skyline = find_skyline(arr, 0 , n - 1 )
skyline.print_skyline()
|
C#
using System;
using System.Collections.Generic;
public class Building {
public int left, ht, right;
public Building( int left, int ht, int right) {
this .left = left;
this .ht = ht;
this .right = right;
}
}
public class Strip {
public int left, ht;
public Strip( int left, int ht) {
this .left = left;
this .ht = ht;
}
}
public class SkyLine {
public List<Strip> arr;
public int capacity, n;
public SkyLine( int cap) {
this .arr = new List<Strip>();
this .capacity = cap;
this .n = 0;
}
public int count() {
return this .n;
}
public SkyLine merge(SkyLine other) {
SkyLine res = new SkyLine( this .n + other.n);
int h1 = 0, h2 = 0, i = 0, j = 0;
while (i < this .n && j < other.n) {
if ( this .arr[i].left < other.arr[j].left) {
int x1 = this .arr[i].left;
h1 = this .arr[i].ht;
int maxh = Math.Max(h1, h2);
res.append( new Strip(x1, maxh));
i++;
} else {
int x2 = other.arr[j].left;
h2 = other.arr[j].ht;
int maxh = Math.Max(h1, h2);
res.append( new Strip(x2, maxh));
j++;
}
}
while (i < this .n) {
res.append( this .arr[i]);
i++;
}
while (j < other.n) {
res.append(other.arr[j]);
j++;
}
return res;
}
public void append(Strip st) {
if ( this .n > 0 && this .arr[ this .n-1].ht == st.ht) {
return ;
}
if ( this .n > 0 && this .arr[ this .n-1].left == st.left) {
this .arr[ this .n-1].ht = Math.Max( this .arr[ this .n-1].ht, st.ht);
return ;
}
this .arr.Add(st);
this .n++;
}
public void printSkyline() {
Console.WriteLine( "Skyline for given buildings is:" );
for ( int i = 0; i < this .n; i++) {
Console.Write( "(" + this .arr[i].left + ", " + this .arr[i].ht + "), " );
}
Console.WriteLine();
}
}
public class SkylineProblem {
public static SkyLine findSkyline(Building[] arr, int l, int h) {
if (l == h) {
SkyLine res2 = new SkyLine(2);
res2.append( new Strip(arr[l].left, arr[l].ht));
res2.append( new Strip(arr[l].right, 0));
return res2;
}
int mid = (l + h) / 2;
SkyLine sl = findSkyline(arr, l, mid);
SkyLine sr = findSkyline(arr, mid+1, h);
SkyLine res3 = sl.merge(sr);
return res3;
}
public static void Main( string [] args) {
Building[] arr = { new Building(1, 11, 5), new Building(2, 6, 7), new Building(3, 13, 9),
new Building(12, 7, 16), new Building(14, 3, 25), new Building(19, 18, 22),
new Building(23, 13, 29), new Building(24, 4, 28)};
SkyLine res1 = findSkyline(arr, 0, arr.Length-1);
res1.printSkyline();
}
}
|
Javascript
const Building = {
left: Number,
ht: Number,
right: Number,
};
class Strip {
constructor(l = 0, h = 0) {
this .left = l;
this .ht = h;
}
}
class SkyLine {
constructor(cap) {
this .capacity = cap;
this .arr = new Array(cap);
this .n = 0;
}
count() {
return this .n;
}
Merge(other) {
let res = new SkyLine( this .n + other.n);
let h1 = 0, h2 = 0;
let i = 0, j = 0;
while (i < this .n && j < other.n) {
if ( this .arr[i].left < other.arr[j].left) {
let x1 = this .arr[i].left;
h1 = this .arr[i].ht;
let maxh = Math.max(h1, h2);
res.append( new Strip(x1, maxh));
i++;
} else {
let x2 = other.arr[j].left;
h2 = other.arr[j].ht;
let maxh = Math.max(h1, h2);
res.append( new Strip(x2, maxh));
j++;
}
}
while (i < this .n) {
res.append( this .arr[i]);
i++;
}
while (j < other.n) {
res.append(other.arr[j]);
j++;
}
return res;
}
append(st) {
if ( this .n > 0 && this .arr[ this .n - 1].ht == st.ht) {
return ;
}
if ( this .n > 0 && this .arr[ this .n - 1].left == st.left) {
this .arr[ this .n - 1].ht = Math.max( this .arr[ this .n - 1].ht, st.ht);
return ;
}
this .arr[ this .n] = st;
this .n++;
}
print() {
let str = '' ;
for (let i = 0; i < this .n; i++) {
str += `(${ this .arr[i].left}, ${ this .arr[i].ht}), `;
}
console.log(`Skyline for given buildings is \n${str}`);
}
}
function findSkyline(arr, l, h) {
if (l == h) {
let res = new SkyLine(2);
res.append( new Strip(arr[l].left, arr[l].ht));
res.append( new Strip(arr[l].right, 0));
return res;
}
let mid = Math.floor((l + h) / 2);
let sl = findSkyline(arr, l, mid);
let sr = findSkyline(arr, mid + 1, h);
let res = sl.Merge(sr);
delete sl;
delete sr;
return res;
}
const main = () => {
const arr = [
{ left: 1, ht: 11, right: 5 },
{ left: 2, ht: 6, right: 7 },
{ left: 3, ht: 13, right: 9 },
{ left: 12, ht: 7, right: 16 },
{ left: 14, ht: 3, right: 25 },
{ left: 19, ht: 18, right: 22 },
{ left: 23, ht: 13, right: 29 },
{ left: 24, ht: 4, right: 28 },
];
const n = arr.length;
const ptr = findSkyline(arr, 0, n - 1);
console.log( "Skyline for given buildings is" );
ptr.print();
};
main();
|
Output Skyline for given buildings is
(1, 11), (3, 13), (9, 0), (12, 7), (16, 3), (19, 18), (22, 3), (23, 13), (29, 0),
Time complexity of above recursive implementation is same as Merge Sort. T(n) = T(n/2) + Θ(n) Solution of above recurrence is Θ(nLogn) References:
This article is contributed Abhay Rathi. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above