对于某个二维平面上的点的集合,列举出给定范围内的点。
首先将问题简化:把点集合从二维空间缩小到一维的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;
}