以log(n)的时间求矩形内的点

来源:互联网 发布:grub2手动引导ubuntu 编辑:程序博客网 时间:2024/05/01 15:55

版权所有,如需转载,请注明出处(http://blog.csdn.net/dengdefang/article/details/13771467)

设想这么一个简单的问题,在一个平面上有n个点,给定一个矩形,问位于矩形内的点有哪些。

这个问题的简单思路非常简单,每次遍历所有点,看其是否在给定的矩形中。时间复杂度呢?单次查询的时间就是一次遍历的时间,也就是O(n),但如果给定的点基本不变,但查询量特别大,每次查询都要以O(n)的复杂度。能不能把给定的数据预处理一下,然后以后每次查询的复杂度降低呢?

一个基本的思路是把相邻的点用最小包围矩形包起来,然后再递归地处理这些矩形,以更大的最小包围矩形包围这些相邻的矩形。下面是个简单的例子:

在查询一个矩形内的所有点时,先把矩形和树的根求交,如果没有交集,说明矩形内没有点。否则说明矩形内可能包含有给定的点,于是就把矩形与根的儿子求交,这样递归下去即可。通过这种方法,我们在建树的时候,用了O(n)的时间,再以后每次查询的时候,大概是log(n)的时间


现在的难题是:如果判断两个点是否相邻?用他们之间的距离来作为标准肯定不合适

希尔伯特距离和希尔伯特树(http://en.wikipedia.org/wiki/Hilbert_R-tree)在这里可以得到很好的应用。什么是希尔伯特距离?

我们把一个给定区域按如下方式来划分,划分完成后,每个点到原点处的线段长度就是希尔伯特距离。如在H2中,点2的希尔伯特距离是2,点6的希尔伯特距离是6.

现在我们把给定的所有点的希尔伯特距离都算出来,然后根据希尔伯特距离来将它们作升序排序,然后就可以建树了


如何算希尔伯特距离呢?暴力方法肯定不行,有个比较好的方法如下:

        

  • For all points, find max_x and max_y.
  • Find the max of max_x and max_y and call this max_xy.
  • If max_xy is a power of two, leave it. Else make it the next higher power of two.
  • For each point:

1.    Initialize w to be max_xy / 2. Dist = 0.

2.    Find the quadrant on a hilbert curve that the point isin.

3.    Dist += (quadrant * w * w)

4.    w becomes w / 2

5.    Calculate xnew and ynew according to the formulas

6.    Repeat steps 2 to 5 until w becomes 0

Quadrant

x_new

y_new

0

y

x

1

x

y - w

2

x - w

y - w

3

w - y - 1

w * 2 - x - 1

代码如下(从网上搜过来的):
/** * Find the Hilbert order (=vertex index) for the given grid cell  * coordinates. * @param x cell column (from 0) * @param y cell row (from 0) * @param r resolution of Hilbert curve (grid will have Math.pow(2,r)  * rows and cols) * @return Hilbert order  */public static int encode(int x, int y, int r) {    int mask = (1 << r) - 1;    int hodd = 0;    int heven = x ^ y;    int notx = ~x & mask;    int noty = ~y & mask;    int temp = notx ^ y;    int v0 = 0, v1 = 0;    for (int k = 1; k < r; k++) {        v1 = ((v1 & heven) | ((v0 ^ noty) & temp)) >> 1;        v0 = ((v0 & (v1 ^ notx)) | (~v0 & (v1 ^ noty))) >> 1;    }    hodd = (~v0 & (v1 ^ x)) | (v0 & (v1 ^ noty));    return interleaveBits(hodd, heven);}/** * Interleave the bits from two input integer values * @param odd integer holding bit values for odd bit positions * @param even integer holding bit values for even bit positions * @return the integer that results from interleaving the input bits * * @todo: I'm sure there's a more elegant way of doing this ! */private static int interleaveBits(int odd, int even) {    int val = 0;    // Replaced this line with the improved code provided by Tuska    // int n = Math.max(Integer.highestOneBit(odd), Integer.highestOneBit(even));    int max = Math.max(odd, even);    int n = 0;    while (max > 0) {        n++;        max >>= 1;    }    for (int i = 0; i < n; i++) {        int bitMask = 1 << i;        int a = (even & bitMask) > 0 ? (1 << (2*i)) : 0;        int b = (odd & bitMask) > 0 ? (1 << (2*i+1)) : 0;        val += a + b;    }    return val;}

之后的建树和搜索就不用多介绍了

原创粉丝点击