使用段树的矩形并集区域

时间:2019-04-16 06:39:01

标签: algorithm geometry computational-geometry

我试图了解可用于计算一组轴对齐矩形的并集面积的算法。

我要遵循的解决方案在这里:http://tryalgo.org/en/geometry/2016/06/25/union-of-rectangles/

我不了解的部分是:

  

段树是此数据结构的正确选择。它具有复杂度O(logn)    用于更新操作和O(1)    用于查询。我们需要使用每个节点的分数来扩展分段树,并具有以下属性。

     
      
  • 每个节点对应一个y间隔,它是该节点范围内所有索引上基本y间隔的并集。
  •   
  • 如果节点值为零,则得分是后代得分的总和(如果节点是叶则为0)。
  •   
  • 如果节点值为正,则得分为与该节点相对应的y间隔的长度。
  •   

我们如何在O(n log n)中实现呢?

我的想法是创建一个线段树,并在扫线时遇到范围(y范围为矩形的高度)时更新每个范围的值。然后对于每个间隔(在已排序的x数组中,两个连续的元素,通过查看段树中所有元素的总和,将Δx乘以该间隔内y范围的总长度)

这仍然会使我们在细分树的基础中具有max(y)-min(y)个元素。

因此,我不确定这是O(n log n)-其中n是矩形的数量。

非常感谢您的帮助。

谢谢!

2 个答案:

答案 0 :(得分:3)

让我们考虑一些简单的情况: enter image description here

根据您的理解,您将创建一个在基础上具有11-1 = 10个节点的段树,如下所示:

enter image description here

请注意,我们的基础节点只有9个,因为第一个节点的间隔为[1,2],下一个节点的间隔为[2,3],依此类推

当您输入某个矩形时,您将根据其y坐标更新其范围,因此在x = 0上遇到第一个矩形后,您的细分树如下所示:

enter image description here

我们还需要使用惰性传播来更新树上的活动间隔,因此所有活动间隔将为总和贡献1。

因此,当前方法的复杂度类似于O(K log K),其中K = max(y)-min(y)

我们可以将其简化为O(n log n),其中n是矩形的数量。

请注意,只有重要的y坐标存在,因此在此示例中1,3,6,11

还要注意,这样的坐标最多2 * n

因此我们可以将所有坐标映射到一些整数,以便它们更适合分段树。

这称为坐标压缩,可以通过以下方式完成:

  1. 将所有y坐标存储在数组中
  2. 对数组进行排序并删除重复项
  3. 使用map或hashMap将原始坐标映射到排序数组中的位置

因此在我们的示例中为:

  1. [1,3,6,11]
  2. 没有可删除的重复项,因此阵列仍[1,3,6,11]
  3. mp[1]=1, mp[3]=2, mp[6]=3, mp[11]=4

因此,现在的算法保持不变,但是我们只能使用基数最多为2 * n个节点的段树。

我们还需要稍微修改段树, 而不是保持打开或关闭哪些y坐标,我们现在保持打开或关闭哪些y坐标的间隔

因此,对于所有y的唯一排序值,我们将具有间隔[y0,y1],[y1,y2],...的节点。

所有节点也将对总和(如果它们在范围内且处于活动状态)贡献y [i] -y [i-1]而不是一个。

所以我们的新细分树将是这样的:

enter image description here

答案 1 :(得分:0)

  

我们如何在O(n log n)中实现这一目标?

考虑每个矩形,您将更新二进制段树中的范围[x,y]。本质上发生的是,你是

  • 搜索x作为树的左边界(log(N)时间)
  • 搜索y作为树的右边界(log(N)时间)

假设您找到的x节点为[x,a],并且其父节点为[z,a],并且该父节点具有同级节点[a,b]。显然,如果y不在[a,b]下,则将覆盖[a,b]的整个范围,因此您要递增此节点,而不是[a,b]子树下的所有单个段节点。

结果,搜索/更新过程看起来像这样。

  • 如果父节点[a,c]有两个子节点[a,b],[b,c],并且左边界x在[a,b]中,则决定是否增加节点[b]中的值,c](取决于y是否大于c)
  • 如果父节点[a,c]有两个子节点[a,b],[b,c],并且右边界y在[b,c]中,则决定是否增加节点[a]中的值,b](取决于x是否小于a)

基本上,在进入一个节点之前,请决定是否更新其同级。

您决定是否更新的节点数是log(N)(对于x)+ log(N)(对于y)。