对于类似地图的类我目前在字典中保存了一些对象,如:
{(3,5):<something>, (1,12):<whatever>, (17,2):<example>}
键表示元组中的x / y坐标。不是这个区域的每个坐标都被占用,也有“洞”。
某些方法需要从该字典中读出一个区域,例如“从(2,5)到(6,10)的所有对象”。我现在的方法是迭代所有键并返回所请求区域中的所有键。由于这些类似地图的词典可以变得非常大,我正在寻找获得这些领域的直接方式。我想到了一个OrdneredDict,所以如果它超过了所要求的区域,我可以停止迭代,但也许有更好的方法?
答案 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)
时间。
这样的事情可行(grid
是dict
),
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))