A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B).
The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.
For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] .
The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour.
For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ].
Notes:
- The number of buildings in any input list is guaranteed to be in the range
[0, 10000]. - The input list is already sorted in ascending order by the left x position
Li. - The output list must be sorted by the x position.
- 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], ...]
Credits:
Special thanks to @stellari for adding this problem, creating these two awesome images and all test cases.
如果按照矩形來處理將會非常麻煩,可以把這些矩形拆成兩個點,一個左上頂點,一個右上頂點,這個題相當於處理2*n個邊的問題,每一個邊有一個x-axis和一個height,把所給的triplet轉換成[x-axis, height],對這些頂點按x-axis排序。然后開始遍歷這些點,用一個最大堆來記錄高度,對於左頂點,將height加入堆中。對於右頂點,從堆中移出height,同時也意味這這個矩形的結束。堆頂是所有頂點中最高的點,是當前圖形的最高位置。只要這個點沒被移出堆,說明這個最高的矩形還沒結束。如果堆頂高度值出現了變化,說明出現了拐點,記錄相應位置到結果中。
具體代碼中,為了在排序后的頂點列表中區分左右頂點,同一個圖形的左右頂點height一個是正數值,一個是負數值。
(1) 自建一個名為Height的數據結構,保存一個building的index和height。約定,當height為負數時表示這個高度為height的building起始於index;height為正時表示這個高度為height的building終止於index。
(2) 對building數組進行處理,每一行[ Li, Ri, Hi ],根據Height的定義,轉換為兩個Height的對象,即,Height(Li, -Hi) 和 Height(Ri, Hi)。 將這兩個對象存入heights這個List中。
(3) 寫個Comparator對heights進行升序排序,首先按照index的大小排序,若index相等,則按height大小排序,以保證一棟建築物的起始節點一定在終止節點之前。
(4) 將heights轉換為結果。使用PriorityQueue對高度值進行暫存。遍歷heights,遇到高度為負值的對象時,表示建築物的起始節點,此時應將這個高度加PriorityQueue。遇到高度為正值的對象時,表示建築物的終止節點,此時應將這個高度從PriorityQueue中除去。且在遍歷的過程中檢查,當前的PriorityQueue的peek()是否與上一個iteration的peek()值(prev)相同,若否,說明出現了拐點,則應在結果中加入[當前對象的index, 當前PriorityQueue的peek()],並更新prev的值。
Java:
class Edge {
int x;
int height;
boolean isStart;
public Edge(int x, int height, boolean isStart) {
this.x = x;
this.height = height;
this.isStart = isStart;
}
}
public List<int[]> getSkyline(int[][] buildings) {
List<int[]> result = new ArrayList<int[]>();
if (buildings == null || buildings.length == 0
|| buildings[0].length == 0) {
return result;
}
List<Edge> edges = new ArrayList<Edge>();
// add all left/right edges
for (int[] building : buildings) {
Edge startEdge = new Edge(building[0], building[2], true);
edges.add(startEdge);
Edge endEdge = new Edge(building[1], building[2], false);
edges.add(endEdge);
}
// sort edges
Collections.sort(edges, new Comparator<Edge>() {
public int compare(Edge a, Edge b) {
if (a.x != b.x)
return Integer.compare(a.x, b.x);
if (a.isStart && b.isStart) {
return Integer.compare(b.height, a.height);
}
if (!a.isStart && !b.isStart) {
return Integer.compare(a.height, b.height);
}
return a.isStart ? -1 : 1;
}
});
// process edges
PriorityQueue<Integer> heightHeap = new PriorityQueue<Integer>(10, Collections.reverseOrder());
for (Edge edge : edges) {
if (edge.isStart) {
if (heightHeap.isEmpty() || edge.height > heightHeap.peek()) {
result.add(new int[] { edge.x, edge.height });
}
heightHeap.add(edge.height);
} else {
heightHeap.remove(edge.height);
if(heightHeap.isEmpty()){
result.add(new int[] {edge.x, 0});
}else if(edge.height > heightHeap.peek()){
result.add(new int[]{edge.x, heightHeap.peek()});
}
}
}
return result;
}
Java:
public class Solution {
public List<int[]> getSkyline(int[][] buildings) {
List<int[]> result = new ArrayList<int[]>();
if (buildings == null || buildings.length == 0 || buildings[0].length == 0) {
return result;
}
List<Height> heights = new ArrayList<Height>();
for (int[] building : buildings) {
heights.add(new Height(building[0], -building[2]));
heights.add(new Height(building[1], building[2]));
}
Collections.sort(heights, new Comparator<Height>() {
@Override
public int compare(Height h1, Height h2) {
return h1.index != h2.index ? h1.index - h2.index : h1.height - h2.height;
}
});
PriorityQueue<Integer> pq = new PriorityQueue<Integer>(1000, Collections.reverseOrder());
pq.offer(0);
int prev = 0;
for (Height h : heights) {
if (h.height < 0) {
pq.offer(-h.height);
} else {
pq.remove(h.height);
}
int cur = pq.peek();
if (cur != prev) {
result.add(new int[]{h.index, cur});
prev = cur;
}
}
return result;
}
class Height {
int index;
int height;
Height(int index, int height) {
this.index = index;
this.height = height;
}
}
}
Python:
class Solution(object):
def getSkyline(self, buildings):
n = len(buildings)
points = sorted([(buildings[i][0], buildings[i][2], 's') for i in range(n)] +
[(buildings[i][1], buildings[i][2], 'e') for i in range(n)])
print points
result, maxHeap = [], []
for p in points:
pre_height = - maxHeap[0] if maxHeap else 0
if p[2] == 's':
heappush(maxHeap, -p[1])
else:
heappop(maxHeap)
cur_height = - maxHeap[0] if maxHeap else 0
if p[2] == 's' and p[1] > pre_height:
result.append([p[0], p[1]])
elif p[2] == 'e' and p[1] > cur_height:
result.append([p[0], cur_height])
return result
Python:
from heapq import heappush, heappop
import functools
class Edge(object):
def __init__(self, x, height, is_start):
self.x = x
self.height = height
self.is_start = is_start
def __repr__(self):
return repr((self.x, self.height, self.is_start))
class Solution1(object):
def compare(self, edge1, edge2):
if edge1.x != edge2.x:
return -1 if edge1.x < edge2.x else 1
if ((edge1.is_start and edge2.is_start) or
(not edge1.is_start and not edge2.is_start)):
return -1 if edge1.height < edge2.height else 1
return -1 if edge1.is_start else 1
def getSkyline(self, buildings):
edges = []
for building in buildings:
start_edge = Edge(building[0], building[2], True)
end_edge = Edge(building[1], building[2], False)
edges.extend((start_edge, end_edge))
sorted_edges = sorted(edges, key=functools.cmp_to_key(self.compare))
print sorted_edges
maxHeap, result = [], []
for edge in sorted_edges:
if edge.is_start:
if not maxHeap or edge.height > - maxHeap[0]:
result.append((edge.x, edge.height))
heappush(maxHeap, - edge.height)
else:
heappop(maxHeap)
if not maxHeap:
result.append((edge.x, 0))
elif edge.height > - maxHeap[0]:
result.append((edge.x, - maxHeap[0]))
return result
Python:
class MaxHeap:
def __init__(self, buildings):
self.buildings = buildings
self.size = 0
self.heap = [None] * (2 * len(buildings) + 1)
self.lineMap = dict()
def maxLine(self):
return self.heap[1]
def insert(self, lineId):
self.size += 1
self.heap[self.size] = lineId
self.lineMap[lineId] = self.size
self.siftUp(self.size)
def delete(self, lineId):
heapIdx = self.lineMap[lineId]
self.heap[heapIdx] = self.heap[self.size]
self.lineMap[self.heap[self.size]] = heapIdx
self.heap[self.size] = None
del self.lineMap[lineId]
self.size -= 1
self.siftDown(heapIdx)
def siftUp(self, idx):
while idx > 1 and self.cmp(idx / 2, idx) < 0:
self.swap(idx / 2, idx)
idx /= 2
def siftDown(self, idx):
while idx * 2 <= self.size:
nidx = idx * 2
if idx * 2 + 1 <= self.size and self.cmp(idx * 2 + 1, idx * 2) > 0:
nidx = idx * 2 + 1
if self.cmp(nidx, idx) > 0:
self.swap(nidx, idx)
idx = nidx
else:
break
def swap(self, a, b):
la, lb = self.heap[a], self.heap[b]
self.lineMap[la], self.lineMap[lb] = self.lineMap[lb], self.lineMap[la]
self.heap[a], self.heap[b] = lb, la
def cmp(self, a, b):
return self.buildings[self.heap[a]][2] - self.buildings[self.heap[b]][2]
class Solution:
def getSkyline(self, buildings):
size = len(buildings)
points = sorted([(buildings[x][0], x, 's') for x in range(size)] +
[(buildings[x][1], x, 'e') for x in range(size)])
maxHeap = MaxHeap(buildings)
ans = []
for p in points:
if p[2] == 's':
maxHeap.insert(p[1])
else:
maxHeap.delete(p[1])
maxLine = maxHeap.maxLine()
height = buildings[maxLine][2] if maxLine is not None else 0
if len(ans) == 0 or ans[-1][0] != p[0]:
ans.append([p[0], height])
elif p[2] == 's':
ans[-1][1] = max(ans[-1][1], height)
else:
ans[-1][1] = min(ans[-1][1], height)
if len(ans) > 1 and ans[-1][1] == ans[-2][1]:
ans.pop()
return ans
C++: heap
class Solution {
private:
enum NODE_TYPE {LEFT, RIGHT};
struct node {
int x, y;
NODE_TYPE type;
node(int _x, int _y, NODE_TYPE _type) : x(_x), y(_y), type(_type) {}
};
public:
vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
vector<node> height;
for (auto &b : buildings) {
height.push_back(node(b[0], b[2], LEFT));
height.push_back(node(b[1], b[2], RIGHT));
}
sort(height.begin(), height.end(), [](const node &a, const node &b) {
if (a.x != b.x) return a.x < b.x;
else if (a.type == LEFT && b.type == LEFT) return a.y > b.y;
else if (a.type == RIGHT && b.type == RIGHT) return a.y < b.y;
else return a.type == LEFT;
});
priority_queue<int> heap;
unordered_map<int, int> mp;
heap.push(0);
vector<pair<int, int>> res;
int pre = 0, cur = 0;
for (auto &h : height) {
if (h.type == LEFT) {
heap.push(h.y);
} else {
++mp[h.y];
while (!heap.empty() && mp[heap.top()] > 0) {
--mp[heap.top()];
heap.pop();
}
}
cur = heap.top();
if (cur != pre) {
res.push_back({h.x, cur});
pre = cur;
}
}
return res;
}
};
C++: Multiset
class Solution {
public:
vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
vector<pair<int, int>> h, res;
multiset<int> m;
int pre = 0, cur = 0;
for (auto &a : buildings) {
h.push_back({a[0], -a[2]});
h.push_back({a[1], a[2]});
}
sort(h.begin(), h.end());
m.insert(0);
for (auto &a : h) {
if (a.second < 0) m.insert(-a.second);
else m.erase(m.find(a.second));
cur = *m.rbegin();
if (cur != pre) {
res.push_back({a.first, cur});
pre = cur;
}
}
return res;
}
};
類似題目:
[LintCode] 131. Building Outline
All LeetCode Questions List 題目匯總

