zoukankan      html  css  js  c++  java
  • 以log(n)的时间求矩形内的点

    设想这么一个简单的问题,在一个平面上有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;
    }

    之后的建树和搜索就不用多介绍了
  • 相关阅读:
    gaia 开源多语言的pipeline 平台
    vernemq 集群 docker-compose 搭建简单试用
    nginx http2 push 试用
    几个方便进行micro frontend 开发的工具&&类库
    Compoxure example 应用说明
    Compoxure 微服务组合proxy 中间件
    SCS Characteristics
    How_Require_Extensions_Work
    nodejs-hook 开发
    microcks 微服务mocks 工具&&运行时
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3400382.html
Copyright © 2011-2022 走看看