💡 This approach uses recursion to merge skylines of subproblems, teaching how to combine solutions and handle complex merges, a useful technique for many interval problems.
Intuition
Divide the list of buildings into two halves, recursively find the skyline for each half, then merge the two skylines into one.
Algorithm
- If the list has one building, return its skyline as two points: start height and end zero.
- Recursively compute skylines for left and right halves.
- Merge the two skylines by iterating through both, comparing x-coordinates and heights, and adding key points when height changes.
- Return the merged skyline.
💡 The merge step is the trickiest, requiring careful handling of overlapping intervals and height comparisons.
def mergeSkylines(left, right):
h1 = h2 = 0
i = j = 0
merged = []
while i < len(left) and j < len(right):
if left[i][0] < right[j][0]:
x, h1 = left[i]
i += 1
elif right[j][0] < left[i][0]:
x, h2 = right[j]
j += 1
else:
x = left[i][0]
h1 = left[i][1]
h2 = right[j][1]
i += 1
j += 1
maxH = max(h1, h2)
if not merged or merged[-1][1] != maxH:
merged.append([x, maxH])
merged.extend(left[i:])
merged.extend(right[j:])
return merged
def getSkyline(buildings):
if not buildings:
return []
if len(buildings) == 1:
L, R, H = buildings[0]
return [[L, H], [R, 0]]
mid = len(buildings) // 2
left = getSkyline(buildings[:mid])
right = getSkyline(buildings[mid:])
return mergeSkylines(left, right)
# Example usage
if __name__ == '__main__':
buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
print(getSkyline(buildings))
Line Notes
if len(buildings) == 1:Base case returns the skyline for a single building
while i < len(left) and j < len(right):Merge two skylines by comparing x-coordinates
maxH = max(h1, h2)Determine the current max height at this x-coordinate
if not merged or merged[-1][1] != maxH:Add key point only if height changes
import java.util.*;
public class SkylineDivideConquer {
public static List<int[]> mergeSkylines(List<int[]> left, List<int[]> right) {
int h1 = 0, h2 = 0, i = 0, j = 0;
List<int[]> merged = new ArrayList<>();
while (i < left.size() && j < right.size()) {
int x = 0;
if (left.get(i)[0] < right.get(j)[0]) {
x = left.get(i)[0];
h1 = left.get(i)[1];
i++;
} else if (right.get(j)[0] < left.get(i)[0]) {
x = right.get(j)[0];
h2 = right.get(j)[1];
j++;
} else {
x = left.get(i)[0];
h1 = left.get(i)[1];
h2 = right.get(j)[1];
i++;
j++;
}
int maxH = Math.max(h1, h2);
if (merged.isEmpty() || merged.get(merged.size() - 1)[1] != maxH) {
merged.add(new int[]{x, maxH});
}
}
while (i < left.size()) merged.add(left.get(i++));
while (j < right.size()) merged.add(right.get(j++));
return merged;
}
public static List<int[]> getSkyline(int[][] buildings) {
if (buildings == null || buildings.length == 0) return new ArrayList<>();
return getSkylineHelper(buildings, 0, buildings.length - 1);
}
private static List<int[]> getSkylineHelper(int[][] buildings, int left, int right) {
if (left == right) {
List<int[]> res = new ArrayList<>();
res.add(new int[]{buildings[left][0], buildings[left][2]});
res.add(new int[]{buildings[left][1], 0});
return res;
}
int mid = (left + right) / 2;
List<int[]> leftSky = getSkylineHelper(buildings, left, mid);
List<int[]> rightSky = getSkylineHelper(buildings, mid + 1, right);
return mergeSkylines(leftSky, rightSky);
}
public static void main(String[] args) {
int[][] buildings = {{2,9,10},{3,7,15},{5,12,12},{15,20,10},{19,24,8}};
List<int[]> skyline = getSkyline(buildings);
for (int[] p : skyline) {
System.out.print("[" + p[0] + "," + p[1] + "] ");
}
}
}
Line Notes
if (left == right)Base case returns skyline for one building
while (i < left.size() && j < right.size())Merge two skylines by comparing x-coordinates
int maxH = Math.max(h1, h2);Calculate max height at current x
if (merged.isEmpty() || merged.get(merged.size() - 1)[1] != maxH)Add key point only if height changes
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<pair<int,int>> mergeSkylines(const vector<pair<int,int>>& left, const vector<pair<int,int>>& right) {
int h1 = 0, h2 = 0, i = 0, j = 0;
vector<pair<int,int>> merged;
while (i < (int)left.size() && j < (int)right.size()) {
int x = 0;
if (left[i].first < right[j].first) {
x = left[i].first;
h1 = left[i].second;
i++;
} else if (right[j].first < left[i].first) {
x = right[j].first;
h2 = right[j].second;
j++;
} else {
x = left[i].first;
h1 = left[i].second;
h2 = right[j].second;
i++;
j++;
}
int maxH = max(h1, h2);
if (merged.empty() || merged.back().second != maxH) {
merged.emplace_back(x, maxH);
}
}
while (i < (int)left.size()) merged.push_back(left[i++]);
while (j < (int)right.size()) merged.push_back(right[j++]);
return merged;
}
vector<pair<int,int>> getSkylineHelper(const vector<vector<int>>& buildings, int left, int right) {
if (left == right) {
return {{buildings[left][0], buildings[left][2]}, {buildings[left][1], 0}};
}
int mid = (left + right) / 2;
auto leftSky = getSkylineHelper(buildings, left, mid);
auto rightSky = getSkylineHelper(buildings, mid + 1, right);
return mergeSkylines(leftSky, rightSky);
}
vector<pair<int,int>> getSkyline(vector<vector<int>>& buildings) {
if (buildings.empty()) return {};
return getSkylineHelper(buildings, 0, buildings.size() - 1);
}
int main() {
vector<vector<int>> buildings = {{2,9,10},{3,7,15},{5,12,12},{15,20,10},{19,24,8}};
auto skyline = getSkyline(buildings);
for (auto& p : skyline) {
cout << "[" << p.first << "," << p.second << "] ";
}
cout << endl;
return 0;
}
Line Notes
if (left == right)Base case returns skyline for one building
while (i < (int)left.size() && j < (int)right.size())Merge two skylines by comparing x-coordinates
int maxH = max(h1, h2);Calculate max height at current x
if (merged.empty() || merged.back().second != maxH)Add key point only if height changes
function mergeSkylines(left, right) {
let h1 = 0, h2 = 0, i = 0, j = 0;
const merged = [];
while (i < left.length && j < right.length) {
let x = 0;
if (left[i][0] < right[j][0]) {
x = left[i][0];
h1 = left[i][1];
i++;
} else if (right[j][0] < left[i][0]) {
x = right[j][0];
h2 = right[j][1];
j++;
} else {
x = left[i][0];
h1 = left[i][1];
h2 = right[j][1];
i++;
j++;
}
const maxH = Math.max(h1, h2);
if (merged.length === 0 || merged[merged.length - 1][1] !== maxH) {
merged.push([x, maxH]);
}
}
while (i < left.length) merged.push(left[i++]);
while (j < right.length) merged.push(right[j++]);
return merged;
}
function getSkyline(buildings) {
if (!buildings.length) return [];
if (buildings.length === 1) {
const [L, R, H] = buildings[0];
return [[L, H], [R, 0]];
}
const mid = Math.floor(buildings.length / 2);
const left = getSkyline(buildings.slice(0, mid));
const right = getSkyline(buildings.slice(mid));
return mergeSkylines(left, right);
}
// Example usage
const buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]];
console.log(getSkyline(buildings));
Line Notes
if (buildings.length === 1)Base case returns skyline for one building
while (i < left.length && j < right.length)Merge two skylines by comparing x-coordinates
const maxH = Math.max(h1, h2);Calculate max height at current x
if (merged.length === 0 || merged[merged.length - 1][1] !== maxH)Add key point only if height changes
Divide and conquer splits the problem and merges skylines in O(n log n) time, similar to merge sort.
💡 This approach is efficient and elegant, but the merge step requires careful implementation.
Interview Verdict: Accepted / Elegant alternative
Good to know as an alternative approach, especially to demonstrate divide and conquer skills.