KDtree(范围搜索)(DSL_2_C:Range Search)

对于某个二维平面上的点的集合,列举出给定范围内的点。

首先将问题简化:把点集合从二维空间缩小到一维的x轴上,考虑如何列举出x轴上给定区域(范围)内的点,即一维的范围搜索

np=0;
make1dtree(0,n);

make1dtree(l,r)
{
    if(!(l<r)){
        return NIL;
    }
    int mid=(l+r)>>1;
    T=np++;
    t[T].location=mid;
    t[T].l=make1dtree(l,mid);
    t[T].r=make1dtree(mid+1,r);
    return T;
}

列举出指定范围内的点

find(v,sx,tx)
{
    x=p[t[v].location].x;
    if(sx<=x&&x<=tx){
        print(p[v].location);
    }
    if(t[v].l!=NIL&&sx<=x){
        find(t[v].l,sx,tx);
    }
    if(t[v].r!=NIL&&x<=tx){
        find(t[v].r,sx,tx);
    }
}

算法进行拓展至K维空间,需要构建名为“KD树”的数据结构,就可以搜索指定区域内的点了。

KD树的生成方法多种多样,针对二维平面的基本方法:K维算法的基本思路与一维算法的一样,都是需要对点进行排序,然后取中间值作为根节点来构建树。

前面处理一维(x轴上的点)问题时,只以x的值为基准进行了排序,但是点分布到二维空间后,我们就需要对x轴和y轴分别排序,至于要选择哪个轴为基准,则是按照树的深度进行循环,比如深度为偶数时以 x轴为基准,为奇数时以y轴为基准,二者交替出现。

make2d(l,r,depth)
{
    if(!(l<r)){
        return NIL;
    }
    int mid=(l+r)>>1;
    T=np++;
    if(depth%2==0){
        以x坐标为基准,将p从l到r的点进行升序排列
    }else{
        以y坐标为基准,将p从l到r的点进行升序排列
    }
    t[T].location=mid;
    t[T].l=make2dtree(l,mid,depth+1);
    t[T].r=make2dtree(mid+1,r,depth+1);
    return T;
}

make2dtree是make1dtree的拓展,在访问结点时多检查了结点的深度depth,跟据深度的奇偶来变更排序基准(x,y轴),find函数也跟据depth来区分两种情况进行搜索。

复杂度:算法在构建树结构时需要进行logn(树的高)次o(logn)的排序,因此复杂度为o(logn)^2.

//KDtree
#include<bits/stdc++.h>

using namespace std;

const int maxn=1e7+10;
const int NIL=-1;

struct node{
    int location,l,r;
};

struct point{
    int id,x,y;
    friend bool operator <(const point &a,const point &b){
        return a.id<b.id;
    }
};

void print(point a)
{
    printf("%d\n",a.id);
}

int n,np;
point p[maxn];
node t[maxn];

bool lessx(const point p1,const point p2){return p1.x<p2.x;}
bool lessy(const point p1,const point p2){return p1.y<p2.y;}
//建造KDtree
int makekdtree(int l,int r,int depth)
{
    if(!(l<r)){
        return NIL;
    }
    int mid=(l+r)>>1;
    int T=np++;
    if(depth%2==0){
        sort(p+l,p+r,lessx);
    }else{
        sort(p+l,p+r,lessy);
    }
    t[T].location=mid;
    t[T].l=makekdtree(l,mid,depth+1);
    t[T].r=makekdtree(mid+1,r,depth+1);
    return T;
}

void find(int v,int sx,int tx,int sy,int ty,int depth,vector<point>&ans)
{
    int x=p[t[v].location].x;
    int y=p[t[v].location].y;
    if(sx<=x&&x<=tx&&sy<=y&&y<=ty){
        ans.push_back(p[t[v].location]);
    }
    if(depth%2==0){
        if(t[v].l!=NIL){
            if(sx<=x){
                find(t[v].l,sx,tx,sy,ty,depth+1,ans);
            }
        }
        if(t[v].r!=NIL){
            if(x<=tx){
                find(t[v].r,sx,tx,sy,ty,depth+1,ans);
            }
        }
    }else{
        if(t[v].l!=NIL){
            if(sy<=y){
                find(t[v].l,sx,tx,sy,ty,depth+1,ans);
            }
        }
        if(t[v].r!=NIL){
            if(y<=ty){
                find(t[v].r,sx,tx,sy,ty,depth+1,ans);
            }
        }
    }
}

int main()
{
    int x,y;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d",&x,&y);
        p[i].id=i;
        p[i].x=x;
        p[i].y=y;
        t[i].l=t[i].r=NIL;
    }
    np=0;
    int root=makekdtree(0,n,0);
    int q;
    scanf("%d",&q);
    int sx,tx,sy,ty;
    vector<point>ans;
    for(int i=0;i<q;i++){
        scanf("%d%d%d%d",&sx,&tx,&sy,&ty);
        ans.clear();
        find(root,sx,tx,sy,ty,0,ans);
        sort(ans.begin(),ans.end());
        for(int j=0;j<ans.size();j++){
            print(ans[j]);
        }
        printf("\n");
    }
    return 0;
}



展开阅读全文

没有更多推荐了,返回首页