从矩形集创建矩形的连续矩阵

时间:2016-04-15 13:33:30

标签: c++ algorithm sorting matrix rectangles

我有一组对象(每个对象包含一个矩形和一个分配给它的值),它保存在一个向量容器中。 见下图: enter image description here

我需要通过在每个y / x左下(LL)/右上(UR)坐标处绘制水平和垂直线来创建矩阵,如下所示: enter image description here

我需要为每个新的空矩形赋予value = 0,并且对于初始矩形内的其他矩形,我需要分配它们的旧值。
我用一些天真的算法实现了这个,但是当我有大量的矩形时它工作得太慢了。我的算法基本上做了以下几点:
- 将所有矩形存储在地图容器中。地图的每个元素都包含一组具有相同LL Y坐标的矩形,它们按LL X坐标排序,即键为LL Y坐标。
- 将所有X / Y坐标存储在集合容器中 - 迭代Y / X坐标容器,并为每个新矩形查明它是否存在于map中,如果存在 - 为其分配现有值,否则 - 赋值0。即,对于每个新矩形,它在地图中查找其LL Y坐标,如果存在这样的Y,则搜索相应的值(矩形集),否则 - 它在整个地图中搜索。

是否有一种有效的算法来获得所需的结果?

3 个答案:

答案 0 :(得分:1)

我怀疑查找和迭代不够快。像“否则它搜索整个地图”这样的事情指出你做了非常繁重的计算。

我认为您需要的是使用二维数据结构。 k-d树或BSP可以工作,但最容易理解和实现的是四叉树。

在四叉树中,每个节点代表空间中的一个矩形。通过选择沿2维的中点并使子代表4个结果矩形,可以将每个节点分成4个子节点。每个节点还保存您要分配给该区域的值,如果值是统一的,则保留额外标志。

要标记具有某个值的矩形,请从​​根目录开始递归:

  • 如果输入矩形覆盖节点矩形,则将值设置为该节点,将其标记为均匀并返回。
  • 如果输入矩形和节点矩形不接触则返回。
  • 如果节点标记为统一,请将值复制到其子节点,并将节点标记为不均匀。
  • 递归调用4个孩子(你可能需要创建它们)。
  • 在返回途中,检查4个孩子是否具有相同的值并且都标记为统一,如果是,则将节点标记为统一并设置与孩子相同的值。

这种方法的主要优点是可以快速标记地图的大部分区域。您还可以证明标记区域为O(logN),其中N是地图的大小(常量树的常数大于)。

您可以在wikipedia上找到更详细的说明和一些有用的图片。

答案 1 :(得分:1)

对于n个矩形,这可以通过查看 O(n ^ 3)时间(或者如果最多有限数量的矩形相交,只需O(n ^ 2)时间)即可轻松解决问题以不同的方式。这应该足以在几秒钟内处理多达数千个矩形。

此外,除非在问题中添加了一些其他约束,否则后一时间限制是最优的:即,存在由n个非交叉矩形组成的输入,其中需要O(n ^ 2)个较小的网格矩形输出(当然需要O(n ^ 2)时间)。这种输入的一个例子是n个宽度为1的矩形,它们都具有相等的最低y值,并具有高度1,2,...,n。

网格大小范围

首先,请注意,最多可以有2n条垂直线,最多2n条水平线,因为每个输入矩形最多引入2种(如果一条或两条垂直线也是一些已经考虑过的矩形的边缘,对于水平线也是如此)。因此,这些行定义的网格中最多可以有(2 * n - 1)^ 2 = O(n ^ 2)个单元格。

网格单元坐标系

我们可以为网格单元创建一个坐标系统,其中每个单元格由其左下角标识,并且两个网格线的交点的坐标简单地由下面的水平网格线的数量给出它和它左边的垂直网格线的数量(使得最底部,最左边的网格单元具有共同点(0,0),其右边的单元格具有共同点(1,0),上面的单元格有两个单元格 小区有共同(1,2)等。)

算法

对于具有LL co-ords(x1,y1)和UR co-ords(x2,y2)的每个输入矩形,我们确定它在新的网格坐标系统中占据的水平和垂直间隔,然后简单迭代属于该矩形区域的每个单元(i,j)(即,每个网格单元(i,j),使得toGridX(x1)< = i< toGridX(x2)和toGridY(y1)< = j < toGridY(y2))具有嵌套的for循环,在散列表中记录(i,j)处的单元格的ID(颜色?)应该是当前输入矩形的颜色。输入矩形应该以递减的z顺序处理(隐含地,至少似乎有这样的顺序,来自你的例子),这样对于由多个输入矩形覆盖的任何单元格,哈希表将结束记录任何&#34 ;最近的"矩形的颜色是。最后,遍历哈希表,将每个网格co-ord对(i,j)转换回与此网格单元格对应的输入空间矩形的LL和UR co-ords,并输出具有给定ID的矩形通过此哈希键的值。

预处理

为了实现上述目标,我们需要两件事:一种将输入空间坐标映射到网格坐标(确定给定输入矩形的水平和垂直网格间隔)的方法,以及一种方法map grid坐标回到输入空间坐标(在最后一步生成输出矩形)。这两项操作都很容易通过旧的主力,排序

给定某个输入矩形的任何一个角(x,y),对应于x的网格x坐标,toGridX(x),只是x内的等级位置排序列表中输入矩形之间存在的所有不同 x垂直边缘的位置。类似地,toGridY(y)只是所有不同y的排序列表中y的排名位置输入矩形中存在的水平边缘的位置。在另一个方向上,对于任何网格坐标(i,j),来自GridX(i)的相应输入空间x坐标简直是任何垂直的第i个最小x坐标(忽略重复)输入矩形之间的边缘,以及fromGridY(j)的边缘。这些都可以按如下方式计算(所有数组索引从0开始,我只显示如何为x co-ords执行; y co-ords类似):

  1. 对于具有LL co-ords(x1,y1)和(x2,y2)的输入中的每个矩形i:
    • 将两元素数组[x1,i]追加到数组列表VERT。
    • 将两元素数组[x2,i]追加到数组列表VERT。
  2. 按照第一项的增加顺序对列表VERT进行排序。
  3. 组合具有相同x个co-ords的VERT中的元素。特别:
    1. 设置j = 0。
    2. 对于i从1到n-1:
      • 如果VERT [i] [0] == VERT [j] [0],则将VERT [i] [1]附加到VERT [j](从而在位置j处形成长度为3或更大的数组),否则设置j = j + 1并用两元素数组VERT [i]覆盖VERT [j]。
    3. 从VERT中删除VERT [j + 1]和所有后来的元素。
  4. 此时,对于任何i,VERT [i]是一个数组,包含(在其第二个和后续位置)每个输入矩形的ID,使用左边或右边的第i个 - 任何输入矩形使用的最左边的不同垂直线 - 或者换句话说,rank-i垂直线。我们现在"反转"这样:

    • 对于i从0到n-1:
      • 对于j从1到长度(VERT [i]) - 1:
        • 设置为GridX [VERT [i] [j]] = i。
  5. 对于i从0到长度(VERT)-1:
    • 设置fromGridX [i] = VERT [i] [0]。
  6. 运行时间

    如前所述,最多有O(n ^ 2)个网格单元。 n个输入矩形中的每一个可以占据最多所有这些单元,每个单元在每个输入矩形中访问一次,持续时间为O(n ^ 3)。请注意,这是一个非常悲观的时间限制,例如,如果没有(或者只有有界数字)的矩形重叠,那么它会下降到O(n ^ 2),因为不会多次访问网格单元格。

答案 2 :(得分:1)

假设您知道最顶部和最底部y以及最左侧和最右侧x,请将属于每个矩形的四个向量扩展到相应的最大值和最小值{{1} }和x点。保留一组扩展的垂直向量和一组扩展的水平向量。每当添加扩展矢量时,它必然与垂直列表中的每个矢量相交 - 交叉点是矩阵的单元坐标。

完成单元格坐标列表后,迭代它们并适当地分配值,查看它们是否在原始矩形内或外。我不太熟悉矩形的数据结构,但在我看来,两个间隔树,一个用于水平,另一个用于垂直,可以在每个查询的y时间找到答案,其中O(log n)是树中的间隔数。

总之,此方法似乎是n时间,其中O(n * log m)是结果矩阵中的单元格坐标数,n是原始矩形的数量。