例如,当在x,y坐标中以(x-left,x-right,y)形式给出点时,(1,5,3),(2,4,5)返回(1,2 ,3),(2,4,5),(4,5,3)
答案 0 :(得分:4)
一个简单的贪心算法可以很好地解决这个问题。
按y坐标(递减)对细分进行排序; ca ;;此列表RecyclerView
。现在...
seg
请注意,间隔删除在某些情况下可能会发生:
top_hull = [empty set]
while seg is not empty
head = seg.pop() // pop off the highest segment.
top_hull += head
for each segment in seg
remove the interval (head.xleft, head.y-left) from segment
取决于您的实现语言,区间代数可能会有出色的支持包。
答案 1 :(得分:2)
修剪答案有一个正确的主意,但我认为这对于解释如何检查间隔重叠是不公平的。实际上,该算法的该部分在二次时间O(n^2)
中运行,因为它在某个点形成了所有n^2
对,这是不必要的。我会做什么-
首先,从细分列表中创建一个最大堆,并以y坐标为键。您可以提取和删除O(logn)
时间中的最大值,因此这与排序一样复杂,只是弹出内置代码即可。
heap = max_heap(segement_list)
output = []
while heap is not empty
segment = heap.pop() # max / highest
# trim / split segment
# append trimmed segment(s)
现在,我们只需要修剪段。无需将其与其他所有线段配对并根据需要进行修剪,我们将使用另一个数据结构来允许我们快速查询潜在的交叉点。我们将每个添加的段存储在二进制搜索树中,并以较低的x坐标为其键。然后,我们可以遍历此树,以查找小于或等于我们要添加的段的最大段(通过较低的x坐标)。
为了使以下各段的技术细节不那么冗长,让我们仅避开两个关键比较的实现细节。假设段a
的{{1}}值比lower_x
小(因为在下面的段落中,我们将始终知道哪个较小)。
b
我们还需要三个转换,使用相同的# boolean- do `a` and `b` intersect
function intersects(a, b)
return a.upper_x >= b.lower_x
# boolean- is `b` a subsegment of `a`
function is_subsegment(a, b)
return a.upper_x >= b.upper_x
和a
定义-
b
返回查询BST片段的想法,即在查询段function merge(a, b)
a.upper_x = b.upper_x
function trim_left(a, b)
a.upper_x = b.lower_x
function trim_right(a, b)
b.lower_x = a.upper_x
时获得的left_segment
,看看它们是否相交。如果它们相交,请检查segment
中的segment
is_subsegment
。如果是,请中止并将left_segment
移至堆中的下一个段。否则,如果它们相交,则需要continue
trim_right
。无论是否有交叉点,我们都将处理任何右侧的交叉点。之后,我们可以将segment
与经过修改的merge
(实际上是segment
,subsegment
重叠)一起left_segment
。
left_segment
是唯一可以从左侧重叠的分段,因为我们将所有重叠的分段合并到BST中时将它们合并。但是,这不适用于右侧,因为我们的segment
尚未从右侧进行修剪。我们将需要逐步处理修剪。
将right_segment
设为树中left_segment
之后的下一段(按顺序遍历)。制作名为segment
的{{1}}的副本。如果subsegment
与subsegment
相交,将其修剪左,将right_segment
插入输出数组,合并两个段,然后从BST中删除subsegment
。否则,只需将right_segment
插入数组。现在我们可以将subsegment
与subsegment
合并(如果它们重叠)。如果没有,请在BST中插入left_segment
,并为其分配变量subsegment
。
现在,我们重复此过程,直到从left_segment
到segment
的{{1}}突围。然后,对堆中的下一个段重复 entire 过程。
我们知道,形成最大堆并弹出最大is_subsegment
次将导致left_segment
时间复杂性。棘手的部分是弄清楚处理交叉口的时间复杂度。请注意,在处理并合并所有n
后,对于我们处理的每个段,我们的BST总体上最多增加了一个。这是因为我们的所有O(nlogn)
每次迭代都会合并在一起,因此最终形成了一个很大的细分。这意味着我们的BST不大于subsegment
,因此查询和删除BST或将其插入需要花费subsegment
的时间。
还要注意,对于插入BST的每个段,它将仅每个与另一个段相交一次-因为当这样做时,这两个(或更多)将合并为一个新的段。例外是,当一个段是其n
的一个子段时,但是在这种情况下,我们中止而始终不发出任何新段,因此它的+0净大小始终会变化。使用此知识,再结合先前的观察结果,即每个段最终最多会为BST贡献一个新段,我们可以得出结论:最多会有O(logn)
个交集,因此会有插入/删除。因此,left_segment
时间来维持我们的BST。
鉴于我们其余的操作都是固定时间,因此在对十字路口进行修剪和修剪时,我们的整体时间复杂度为O(n)
,而不是O(nlogn)
。
答案 2 :(得分:2)
解决此问题的最简单有效(O(N log N))方法是使用“线扫”算法。
想象一下,在整个集合中从左向右扫过一条垂直线,并跟踪它穿过的最上段。每当最上面的航段发生变化时,这都是可能影响到顶部船身的重要事件。从这些更改的列表中计算顶部船体将很容易。
请注意,这些重要事件只能在输入段之一开始或结束的x位置发生。因此,我们只需要考虑在这x个位置会发生什么,而不是平稳地扫过该线。所以: