最小边界四叉树节点

5

我正在编写一个基于整数的四叉树结构,它是从节点向上构建的而不是向下。为了做到这一点,我需要找到包含所有元素的下一个最大节点。如果我已经定义了一个现有节点,并尝试添加一个超出该节点边界的项,则需要创建一个更大的节点来包含它们两个。我已经(我认为很聪明)编写了代码以找到单个点周围的边界框:

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

注意,点(0,0)周围的框是大小为(1,1)位于(0,0)位置的框,而不是位于(-.5,-.5)的框,因为它是基于整数的。
据我所知,这将始终返回一个适合作为节点的四叉树的框。例如,new Rectangle(0,0,2,2)是可接受的,new Rectangle(2,2,2,2)也是可接受的,但new Rectangle(1,1,2,2)则不行。
我的问题是,我无法弄清如何为矩形而不是点完成此操作。我能想到的唯一解决方案是循环遍历不断增加的框,但我确定一定有一些O(1)的解决方案,只是我无法想出来。 示例:
Rectangle(X,Y,1,1) -> Rectangle(X,Y,1,1)
Rectangle(0,0,2,2) -> Rectangle(0,0,2,2)
Rectangle(1,1,2,2) -> Rectangle(0,0,4,4)
Rectangle(1,1,3,3) -> Rectangle(0,0,4,4)
Rectangle(0,5,2,2) -> Rectangle(0,4,4,4)
Rectangle(3,3,2,2) -> Rectangle(0,0,8,8)

实施:

private static int BitScanReverse(int mask)
{
    int index = 0;
    while (mask > 1)
    {
        mask >>= 1;
        index++;
    }
    return index;
}

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

public static Rectangle BoundingRectangle(Rectangle r, int magnitude)
{
    int msb = BitScanReverse((r.X ^ (r.X + r.Width - 1)) | (r.Y ^ (r.Y + r.Height - 1)));
    return BoundingRectangle(r.Location, Math.Max(msb + 1, magnitude));
}

1
太棒了,如果这个问题还没有被回答,我会在家里解决它。 - mqp
谢谢。这个问题困扰了我一段时间。我想向 SO 上更聪明的人求助。=] - dlras2
你打算如何处理覆盖多个四叉树节点的矩形?例如 (1,1,3,3) 覆盖了宽度/高度为 2 的 4 个节点。如果你想说它在 (0,0,4,4) 内,那么你将总是在树的根部(最大的节点)有小的东西。 - phkahler
你还能把它放在哪里呢?矩形(1,1,3,3)将被放置在节点(0,0,4,4)中,然后如果添加了一个矩形(0,4,2,2),它将被放置在节点(0,4,4,4)中,这两个节点都将被放置在新的根节点(0,0,8,8)中。 - dlras2
矩形(1,1,1,1)不应该映射到矩形(1,1,1,1)吗? - Tom Sirgedas
@Tom - 你说得对,我不知道我在想什么。 - dlras2
1个回答

3

首先考虑一维情况。我们不是有一个矩形,而是有一个偏移量和长度。

边界矩形的“量级”告诉你要忽略多少位。

假设偏移量为1234,长度为56。我们希望忽略足够的比特位,使得“偏移量”(1234)和“偏移量+长度-1”(1289)映射到同一个数字。

1234 = 10011010010
1289 = 10100001001

显然,我们需要忽略这里除了前两位以外的所有位(忽略9位)。

我们可以通过1234 XOR 1289(结果为475)来编程实现此操作。

1234 = 10011010010
1289 = 10100001001
 475 = 00111011011

接下来需要找出475中最重要的一位。大多数处理器都有此指令(在Windows上,它是_BitScanReverse)。

对于您的二维情况,我们需要获取X轴和Y轴的异或值。然后,我们将这两个结果进行OR运算。最后,找到最重要的一位。

因此,在伪代码中:

magnitude = MSBof( ( X ^ (X+width-1) ) | ( Y ^ (Y+height-1) ) )

要获取实际的矩形,请使用您发布帖子中的函数。传入新的Point(X, Y)。

这看起来不错,但我找不到在C#中实现MSBof的方法,而不需要大量的位操作。有什么建议吗? - dlras2
好的建议。我选择了位运算,但是它非常有效。我在上面发布了我的实现。 - dlras2
好的,太棒了!对于这个实现,有一个小问题:请使用“while (mask > 0)”而不是“while (mask > 1)”。这样你就不需要后来再加上+1来修复它了。这里有其他的实现方法:http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog - Tom Sirgedas
我已经完成了我正在工作的结构,并且一直在很好地使用这种方法。再次感谢,如果您想评论我的类,请在此答案中找到它:https://dev59.com/X07Sa4cB1Zd3GeqP6L-I#3379594 - dlras2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接