LeetCode动画 | 218.天际线问题

今天分享一个LeetCode题,题号是218,标题是天际线问题,题目标签是线段树和Line Sweep [ 扫描线算法 ] ,题目难度是困难。最近新学了Go语言,来尝试一下效果,同时后面也贴出了Java代码【线段树和线扫描】。

题目描述

城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。现在,假设您获得了城市风光照片(图A)上显示的所有建筑物的位置和高度,请编写一个程序以输出由这些建筑物形成的天际线(图B)。

天际线

每个建筑物的几何信息用三元组 [Li,Ri,Hi] 表示,其中 Li 和 Ri 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。可以保证 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX 和 Ri - Li > 0。您可以假设所有建筑物都是在绝对平坦且高度为 0 的表面上的完美矩形。

例如,图A中所有建筑物的尺寸记录为:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] 。

输出是以 [ [x1,y1], [x2, y2], [x3, y3], ... ] 格式的“**关键点**”(图B中的红点)的列表,它们唯一地定义了天际线。**关键点是水平线段的左端点**。请注意,最右侧建筑物的最后一个关键点仅用于标记天际线的终点,并始终为零高度。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。

例如,图B中的天际线应该表示为:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]。

说明:

任何输入列表中的建筑物数量保证在 [0, 10000] 范围内。

输入列表已经按左 x 坐标 Li  进行升序排列。

输出列表必须按 x 位排序。

输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]

解题

光看题目描述似乎觉得很难,但是学习过线段树就会觉得这道题变得很容易。不信,来试试看下面的图:

线段树

我们可以把输入列表作为一个顶点,按照输入列表的长度选取中间的值,建议使用这个方式: mid := l + (r-l)/2 选择中间值,然后进行分治算法。

直到当前输入列表的长度为1,说明不能再分了,在这个地方作为结束条件,然后返回到另外路径划分其它的输入列表。

例如我们划分到 [[2 9 10]] 的时候,当前输入列表的长度为1,不能再进行分治了。

[[2 9 10]]表示的是一个建筑物,分别是左右边缘的横坐标和高度。题目已经将天际线定义为水平线左端点的集合,如[[2 9 10]]关键点集合为[[2 10] [9 0]],分别是一个建筑物上的左上端点和右下端点。

同理,[[3 7 15]]的关键点集合为[[3 15] [7 0]]。

关键的一点来了,我们得到了[[2 9 10]] 和 [[3 7 15]] 两个集合之后,要求在满足题目天际线情况下,怎么把这两个集合进行合并呢?意思是合并之后的集合,也是满足天际线的,如下面合并的过程:

合并

其实我们在题目标签看到了Line Sweep,[ 线扫描或扫描线 ] ,扫描线可以想象成一条向右扫过平面的竖直线,也是一个算法,一般是玩图形学的。

接着上面的步骤,可以通过扫描线算法将两个关键点集合进行合并。

如下图,扫描线从两个集合的起始点,同时向右移动,接触到第一个关键点,则判断这一个关键点是不是满足天际线的,如果是,则将这个关键点添加到“父”集合中;如果不是,则继续同时移动到下一个关键点。

扫描线

但如何判断是否是属于“父”集合中的关键点呢?可以创建两个集合(“子”)的目前高度,然后多方角度找到满足关键点的条件。

扫描线移到[2 10]关键点时,10要大于rpre的,可以满足;

扫描线移到[3 15]关键点时,lpre此时目前的高度为10,而15要大于10的,可以满足;

扫描线移到[7 10]关键点时,rpre大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值