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

版权所有,如需转载,请注明出处(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;
}

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值