对于某个二维平面上的点的集合,列举出给定范围内的点。
首先将问题简化:把点集合从二维空间缩小到一维的x轴上,考虑如何列举出x轴上给定区域(范围)内的点,即一维的范围搜索
np=0;
make1dtree(0,n);
make1dtree(l,r)
{
if(!(l>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>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
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>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&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;ians;
for(int i=0;i