用于保存和查询坐标的Performant python数据结构

时间:2014-04-26 19:13:42

标签: python data-structures dictionary

对于类似地图的类我目前在字典中保存了一些对象,如:

{(3,5):<something>, (1,12):<whatever>, (17,2):<example>}

键表示元组中的x / y坐标。不是这个区域的每个坐标都被占用,也有“洞”。

某些方法需要从该字典中读出一个区域,例如“从(2,5)到(6,10)的所有对象”。我现在的方法是迭代所有键并返回所请求区域中的所有键。由于这些类似地图的词典可以变得非常大,我正在寻找获得这些领域的直接方式。我想到了一个OrdneredDict,所以如果它超过了所要求的区域,我可以停止迭代,但也许有更好的方法?

2 个答案:

答案 0 :(得分:2)

所描述的问题是使用K-D tree可以有效解决的典型问题。

k-d树(k维树的缩写)是用于组织k维空间中的点的空间划分数据结构。在您的情况下,k等于2.它基本上是一个二叉搜索树,您可以在其中在每个级别进行的比较之间进行切换。例如,如果在某个级别上您根据x坐标比较点,那么在下一个级别上,您将使用y坐标比较点。

因此,在O(logN)中可以在树中插入一个点。插入可以通过此图像演示。因此,在数据结构中插入N个点将花费O(NlogN)时间。

图像取自http://coursera.cs.princeton.edu/algs4/assignments/kdtree.html

要查找给定查询矩形中包含的所有点,请从根开始,并使用以下修剪规则递归搜索两个子树中的点:如果查询矩形不与对应于节点的矩形相交,则不需要探索该节点(或其子树)。仅当子树可能包含查询矩形中包含的点时才会搜索子树。

因此,以下running Java code以最佳可能的方式解决了这个问题。通常可以在O(R+logN)时间内回答矩形中包含的点的每个查询。其中R是范围内的点数,N是点数。然而,病态数据的最坏情况运行时间为O(R + sqrt(N))

    void run(){
        Scanner in = new Scanner(System.in);
        int numQueries = in.nextInt();
        Point2D root = null;
        for(int i=0; i<numQueries; i++){
            int type = in.nextInt();
            if(type == 0){   // Add a point
                double x = in.nextDouble();
                double y = in.nextDouble();
                root = addPoint(root, x, y, true);
            }else{
                double x1  = in.nextDouble();
                double y1 = in.nextDouble();
                double x2  = in.nextDouble();
                double y2 = in.nextDouble();
                System.out.println("the points in between the rectange with end points " + 
                     new Point2D(x1, y1) + " and " + new Point2D(x2, y2) + " are :");
                printPointsInRange(root, x1, y1, x2, y2, true);
            }
        }
    }


    // prints all points in the rectangle which has top left coordinates
    // (x1, y1) and bottom right coordinates (x2, y2)
    void printPointsInRange(Point2D root,
                        double x1, double y1, 
                        double x2, double y2, boolean vertical){
        if(root==null){
            return;
        }
        double x = root.x;
        double y = root.y;
        boolean outsideRange = Double.compare(x, x1)<0 || 
                                Double.compare(x, x2)>0 ||
                                 Double.compare(y, y1)>0 || 
                                 Double.compare(y, y2)<0;
        if(!outsideRange){
            System.out.println(root);
        }

        if(vertical){
            if(Double.compare(x, x1)<=0){ 
                // root lies left of left boundary or on the boundary
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }else if(Double.compare(x, x2)>0){
                // root lies right of right boundary
                printPointsInRange(root.left, x1, y1, x2, y2, !vertical);
            }else if(Double.compare(x, x2)==0){
                // root lies on right boundary
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }else{
                // root lies in between x1 and x2
                printPointsInRange(root.left, x1, y1, x2, y2, !vertical);
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }
        }else{
            if(Double.compare(y, y2)<=0){ 
                // root lies below bottom boundary or on bottom boundary
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }else if(Double.compare(y, y1)>0){
                // root lies above top boundary
                printPointsInRange(root.left, x1, y1, x2, y2, !vertical);
            }else if(Double.compare(y, y1)==0){
                // root lies on top boundary
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }else{
                // root lies in between y1 and y2
                printPointsInRange(root.left, x1, y1, x2, y2, !vertical);
                printPointsInRange(root.right, x1, y1, x2, y2, !vertical);
            }
        }
    }

    Point2D addPoint(Point2D root, double x, double y, boolean vertical){
        if(root==null){
            return new Point2D(x, y);
        }
        if(vertical){   // vertical division
            double compare = Double.compare(root.x, x);
            if(compare<0){ // point is on left of root
                root.left = addPoint(root.left, x, y, !vertical);
            }else{        // point is on right of root or on root's x
                root.right = addPoint(root.right, x, y, !vertical);
            }
        }else{
            double compare = Double.compare(y, root.y);
            if(compare>0){    // point is above the root
                root.right = addPoint(root.right, x, y, !vertical);
            }else{            // point is below the root or on root's y 
                root.left = addPoint(root.left, x, y, !vertical);
            } 
        }
        return root;
    }

答案 1 :(得分:1)

您可以生成所有潜在坐标并检查它们是否存在于dict中,而不是遍历整个dict,搜索这些坐标。对于您当前的O(1)时间解决方案,这将是O(n)时间。

这样的事情可行(griddict),

def subset_coordinates(grid, top_left, bottom_right):
    a, b = top_left
    c, d = bottom_right
    for i in range(a, c+1):
        for j in range(b, d+1):
            value = grid.get((i, j))
            if value is not None:
                yield value

objects_in_subset = subset_coordinates(grid, (2, 5), (6, 10))