Open In App

The Skyline Problem | Set-1

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 a common bottom and every building is represented by a triplet (left, ht, right)

A skyline is a collection of rectangular strips. A rectangular strip is represented as a pair (left, ht) where left is x coordinate of the left side of the strip and ht is the height of the strip.

Examples:

Input: 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: { {1, 11}, {3, 13}, {9, 0}, {12, 7}, {16, 3}, {19, 18}, {22, 3}, {23, 13}, {29, 0} }

Explanation:

The skyline is formed based on the key-points (representing by “green” dots)
eliminating hidden walls of the buildings.

The Skyline Problem

Input: buildings[ ][ ] = { {1, 11, 5} }

Output: { {1, 11}, {5, 0} }

Skyline Problem Using Divide and Conquer

We can find Skyline 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. 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.

Below is implementation of above idea. 

C++

// A divide and conquer based C++
// program to find skyline of given buildings
#include <iostream>
using namespace std;

// A structure for building
struct Building {
    // x coordinate of left side
    int left;

    // height
    int ht;

    // x coordinate of right side
    int right;
};

// A strip in skyline
class Strip {
    // x coordinate of left side
    int left;

    // height
    int ht;

public:
    Strip(int l = 0, int h = 0)
    {
        left = l;
        ht = h;
    }
    friend class SkyLine;
};

// Skyline: To represent Output(An array of strips)
class SkyLine {
    // Array of strips
    Strip* arr;

    // Capacity of strip array
    int capacity;

    // Actual number of strips in array
    int n;

public:
    ~SkyLine() { delete[] arr; }
    int count() { return n; }

    // A function to merge another skyline
    // to this skyline
    SkyLine* Merge(SkyLine* other);

    // Constructor
    SkyLine(int cap)
    {
        capacity = cap;
        arr = new Strip[cap];
        n = 0;
    }

    // Function to add a strip 'st' to array
    void append(Strip* st)
    {
        // Check for redundant strip, a strip is
        // redundant if it has same height or left as
        // previous
        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++;
    }

    // A utility function to print all strips of
    // skyline
    void print()
    {
        for (int i = 0; i < n; i++) {
            cout << " (" << arr[i].left << ", " << arr[i].ht
                 << "), ";
        }
    }
};

// This function returns skyline for a
// given array of buildings arr[l..h].
// This function is similar to mergeSort().
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;

    // Recur for left and right halves
    // and merge the two results
    SkyLine* sl = findSkyline(arr, l, mid);
    SkyLine* sr = findSkyline(arr, mid + 1, h);
    SkyLine* res = sl->Merge(sr);

    // To avoid memory leak
    delete sl;
    delete sr;

    // Return merged skyline
    return res;
}

// Similar to merge() in MergeSort
// This function merges another skyline
// 'other' to the skyline for which it is called.
// The function returns pointer to the
// resultant skyline
SkyLine* SkyLine::Merge(SkyLine* other)
{
    // Create a resultant skyline with
    // capacity as sum of two skylines
    SkyLine* res = new SkyLine(this->n + other->n);

    // To store current heights of two skylines
    int h1 = 0, h2 = 0;

    // Indexes of strips in two skylines
    int i = 0, j = 0;
    while (i < this->n && j < other->n) {
        // Compare x coordinates of left sides of two
        // skylines and put the smaller one in result
        if (this->arr[i].left < other->arr[j].left) {
            int x1 = this->arr[i].left;
            h1 = this->arr[i].ht;

            // Choose height as max of two heights
            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++;
        }
    }

    // If there are strips left in this
    // skyline or other skyline
    while (i < this->n) {
        res->append(&arr[i]);
        i++;
    }
    while (j < other->n) {
        res->append(&other->arr[j]);
        j++;
    }
    return res;
}

// Driver Function
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]);

    // Find skyline for given buildings
    // and print the skyline
    SkyLine* ptr = findSkyline(arr, 0, n - 1);
    cout << " Skyline for given buildings is \n";
    ptr->print();
    return 0;
}

Java

// A divide and conquer based Java
// program to find skyline of given buildings
import java.util.*;
// A class for building
class Building {
    int left, ht, right;

    public Building(int left, int ht, int right) {
        this.left = left;
        this.ht = ht;
        this.right = right;
    }
}
// A strip in skyline
class Strip {
    int left, ht;

    public Strip(int left, int ht) {
        this.left = left;
        this.ht = ht;
    }
}
// Skyline: To represent Output(An array of strips)
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;
    }

// A function to merge another skyline
    // to this skyline

    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;
    }

// Function to add a strip 'st' to array
    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++;
    }

// A utility function to print all strips of
    // skyline
    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();
    }
}

// This function returns skyline for a
// given array of buildings arr[l..h].
// This function is similar to mergeSort().
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;
        
        
    // Recur for left and right halves
    // and merge the two results
        SkyLine sl = findSkyline(arr, l, mid);
        SkyLine sr = findSkyline(arr, mid+1, h);
        SkyLine res = sl.merge(sr);
        return res;
    }
    
// Driver Code
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)};

// Find skyline for given buildings
    // and print the skyline
SkyLine res = findSkyline(arr, 0, arr.length-1);
res.printSkyline();
}
}

/* Output:
Skyline for given buildings is:
(1, 11), (3, 13), (9, 0), (12, 18), (22, 3), (25, 0), (28, 4), (29, 0),
*/

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#

// A divide and conquer based Java
// program to find skyline of given buildings
using System;
using System.Collections.Generic;

// A class for building
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;
    }
}

// A strip in skyline
public class Strip {
    public int left, ht;

    public Strip(int left, int ht) {
        this.left = left;
        this.ht = ht;
    }
}

// Skyline: To represent Output(An array of strips)
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;
    }

// A function to merge another skyline
    // to this skyline
    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;
    }

// Function to add a strip 'st' to array
    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++;
    }

// A utility function to print all strips of
    // skyline
    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();
    }
}

// This function returns skyline for a
// given array of buildings arr[l..h].
// This function is similar to mergeSort().
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;
        
    // Recur for left and right halves
    // and merge the two results
        SkyLine sl = findSkyline(arr, l, mid);
        SkyLine sr = findSkyline(arr, mid+1, h);
        SkyLine res3 = sl.merge(sr);
        return res3;
    }
    
// Driver Code
    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)};

    // Find skyline for given buildings and print the skyline
    SkyLine res1 = findSkyline(arr, 0, arr.Length-1);
    res1.printSkyline();
}
}

Javascript

// Js program to find skyline of given buildings

// A structure for building
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;
        // Merge two skylines by comparing the left coordinates of the strips
        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++;
            }
        }
        // Append remaining strips from skyline 1 and 2
        while (i < this.n) {
            res.append(this.arr[i]);
            i++;
        }
        while (j < other.n) {
            res.append(other.arr[j]);
            j++;
        }
        return res;
    }
    // Append a strip to skyline if it is not redundant
    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 the skyline strips
    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) {
        // Base case: when a single building is left
        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;
    }
    // Divide and Conquer approach
    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: O(n logn)
Auxilary Space:O(n)

Refer to the below article for solving the above problem using multiset


Article Tags :