我试图计算n个矩形的并集的周长,其中我有左下角和右上角。每个矩形都位于x轴上(每个矩形的左下角是(x,0))。我一直在研究不同的方法,看起来Sweep-Line算法是最好的方法。我也看过Graham Scan。我的目标是O(n log n)算法。老实说虽然我迷失了如何继续,但我希望这里有人可以尽我所能为我愚蠢,并试图帮助我明白如何实现这一目标。
我从已经完成的研究中收集了一些东西:
我们需要对点进行排序(我不确定我们对它们进行排序的标准)。
我们将分裂并征服某些东西(以实现O(log n))。
我们需要计算交叉点(最好的方法是什么?)
我们需要某种数据结构来保存点(也许是二叉树?)
我最终将用Java实现这个算法。
答案 0 :(得分:3)
该算法是一个繁琐的案例分析。不是很复杂,但很难完全正确。
假设所有矩形都通过左下角和右上角(x0,y0,x1,y1)存储在数组A中。因此,我们可以将矩形的任何边缘表示为一对(e,i),其中对于左,右,顶部和底部边缘的{L,R,T,B}中的e \,并且i表示A [i]。将所有对(L,i)放入开始列表S并在A [i] .x0上对其进行排序。
我们还需要扫描线C,其为顶边的三元组(T,i,d)和底部的(B,i,d)的BST。这里我是一个矩形索引,d是整数深度,如下所述。 BST的关键是边缘' y坐标。最初它是空的。
请注意,您可以随时按顺序遍历C并确定扫描线的哪些部分被矩形隐藏而不是。通过保持深度计数器来做到这一点,最初为零。从最小y到最大,当遇到底边时,将1添加到计数器。当您看到上边缘时,递减1.对于计数器为零的区域,扫描线是可见的。否则它被一个矩形隐藏。
现在你从未真正完成整个遍历。相反,通过逐步保持深度可以提高效率。 C中每个三元组的d元素是其上方区域的深度。 (C中第一个边缘下方的区域总是深度为0.)
最后我们需要一个输出寄存器P.它存储一组折线(边缘的双重链接列表对此很方便)并允许查询形式"给我所有折线的末端' y坐标落在[y0..y1]范围内。算法的一个特性是这些多义线总是有两条横跨扫描线的水平边作为它们的末端,而所有其他边都留在扫描线的左边。此外,没有两条折线相交。它们是输出多边形的一部分"正在构建中。"注意输出多边形可能是非简单的,由多个"循环组成"和#34;洞。"另一个BST将为P做。它最初也是空的。
现在算法看起来大致如此。我不会偷走搞清楚细节的所有乐趣。
{{1}}
随着P的更新,您将生成复杂的多边形。最右边应该关闭最后一个循环。
最后,请注意重合边可能会产生一些棘手的特殊情况。当你碰到那些,再次发布,我们可以讨论。
排序的运行时间当然是O(n log n),但更新扫描线的成本取决于多边形可以重叠的数量:退化情况的O(n)或O(n ^ 2)整个计算。
祝你好运。我已经实现了这个算法(几年前)和其他一些类似的算法。他们在严格的逻辑案例分析中进行了大量的练习。非常令人沮丧,但是当你获胜时也会有所回报。答案 1 :(得分:1)
诀窍是首先找到沿x轴的每个段的最大高度(见上图)。一旦你知道这一点,那么外围很容易:
注意:我没有测试过代码,因此可能存在拼写错误。
filter
总而言之,您需要先计算// Calculate perimeter given the maxY at each line segment.
double calcPerimeter(List<Double> X, List<Double> maxY) {
double perimeter = 0;
for(int i = 1; i < X.size(); i++){
// Add the left side of the rect, maxY[0] == 0
perimeter += Math.abs(maxY.get(i) - maxY.get(i - 1))
// add the top of the rect
perimeter += X.get(i) - X.get(i-1);
}
// Add the right side and return total perimeter
return perimeter + maxY.get(maxY.size() - 1);
}
和X
。完整代码看起来像这样:
maxY