给定二维城市中的 n 个矩形建筑物,计算这些建筑物的天际线,消除隐藏线。主要任务是从侧面查看建筑物并删除所有不可见的部分。
所有建筑物共用一个底部,每个建筑物都由三元组(左,ht,右)表示
- ‘left’:是左侧(或墙壁)的 x 坐标。
- ‘right’: 是右侧的 x 坐标
- ‘ht’:是建筑物的高度。
天际线是矩形条带的集合。矩形条带表示为一对 (left, ht),其中 left 是条带左侧的 x 坐标,ht 是条带的高度。
例子:
输入:建筑物数组 { (1, 11, 5), (2, 6, 7), (3, 13, 9), (12, 7, 16), (14, 3, 25), (19, 18) , 22), (23, 13, 29), (24, 4, 28) } 输出:Skyline(矩形条的数组)一条条具有左侧的 x 坐标和高度 (1, 11), (3, 13) ), (9, 0), (12, 7), (16, 3), (19, 18), (22, 3), (25, 0) 下图是输入1: 当只有一个建筑物时,请考虑以下作为另一个示例输入:{(1, 11, 5)} 输出:(1, 11), (5, 0)
一个简单的解决方案是将天际线或结果初始化为空,然后将建筑物逐一添加到天际线。通过首先找到重叠的条带来添加建筑物。如果没有重叠的条带,新建筑会添加新的条带。如果发现重叠条带,则现有条带的高度可能会增加。此解决方案的时间复杂度为 O(n 2 )
我们可以使用 Divide and Conquer在 Θ(nLogn) 时间内找到 Skyline。这个想法类似于合并排序,将给定的建筑物集划分为两个子集。递归构建两半的天际线,最后合并两条天际线。
如何合并两条天际线?
思路类似于merge sort的merge,从两条天际线的第一条开始,比较x坐标。选择 x 坐标较小的条带并将其添加到结果中。添加条带的高度被认为是从 skyline1 和 skyline2 的当前高度的最大值。
显示合并工作的示例:
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.
下面是上述想法的 C++ 实现。
// A divide and conquer based C++
// program to find skyline of given buildings
#include
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;
}
输出:
Skyline for given buildings is
(1, 11), (3, 13), (9, 0), (12, 7), (16, 3), (19, 18),
(22, 3), (23, 13), (29, 0),
上述递归实现的时间复杂度与归并排序相同。
T(n) = T(n/2) + Θ(n)
上述递归的解为 Θ(nLogn)
参考:
- http://faculty.kfupm.edu.sa/ics/darwish/stuff/ics353handouts/Ch4Ch5.pdf
- www.cs.ucf.edu/~sarahb/COP3503/Lectures/DivideAndConquer.ppt
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。