高等数据结构——kD Tree

从拥有多个属性的报表集合(数据库)中,寻找具有特定属性且位于指定范围内的元素,这类问题称为范围搜索。

编写程序,对魔某个二维平面上点的集合,列举出给定范围内的点。另外,给定的点集合无法进行点的添加和删除操作(静态)。

输入
    n
    x0 y0
    ...
    xn-1 yn-1
    q
    sx0 tx0 sy0 ty0
    ...
    sxq-1 txq-1 syq-1 tyq-1
输出
    对于每个区域,按编号升序输出点集合中满足sxi≤x≤txi且syi≤y≤tyi的点的编号
    0≤n≤500000
    0≤q≤20000
    -1000000000≤x,y,sx,tx,sy,ty≤1000000000
    sx≤tx
    sy≤ty

示例
    6
    2 1
    2 2
    4 2
    6 2
    3 3
    5 4
    2
    2 4 0 4
    4 10 2 5
示例输出
    0
    1
    2    
    4
    
    2
    3    
    5

对于这个问题我们利用划归思想,将2D转化为1D来考虑一下。对于一维,就变成了一维的搜索,我们可以使用二叉树的结构来存储数据。将集合{1,3,5,6,10,13,14,16,19,21}绘制成二叉树。

树节点号3215408769
编号/数值0/11/32/53/64/105/136/147/168/199/21

表中的树节点顺序为前序遍历,编号顺序为中序遍历,构造起来如图所示。

np = 0//初始化编号
make1DTree(0,n)

make1DTree(l,r)
    if!(l<r)
        return NIL

以x坐标为基准将p从l到r的点排序,按升序。

mid=(l+r)/2

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[T[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树”的数据结构,就可以搜索指定的区域了。接下来看看二维平面内如何对点进行搜索吧。对于一维的我们只需对x进行排序,而二维的话我们要对x,y分别排序,按照树的深度进行循环。比如深度为偶数时以x为基准,为奇数时以y为基准,二者交替出现。此过程像画网格一般。

查找时也需要按照深度偶x奇y的规则查找。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
//定义结点信息 
class Node{
	public:
		int location;
		int p,l,r;
		Node(){}
};
//定义点信息
class Point{
	public:
		int id,x,y;//(编号,横坐标,纵坐标)
		Point(){}
		Point(int id,int x,int y){
			this->id=id;
			this->x=x;
			this->y=y;
		}
		bool operator < (const Point &p) const{
			return id<p.id;
		}	
		
		void print(){
			printf("%d\n",id);
		}	
}; 
static const int MAX = 1000000;
static const int NIL = -1;
int N;
Point P[MAX];
Node T[MAX];
int np;
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; 
} 

int makeKDTree(int l,int r,int depth){
	if(!(l<r))
		return NIL;
	int mid=(l+r)/2;
	int t=np++;//树节点的编号 
	//二维空间的区域划分(x,y交替分割) 
	//将x看做左右y看做上下
	//先将平面按照x分成左右字面
	//接着再把分好的左右字面分成上下字面 
	if(depth%2==0){
		sort(P+l,P+r,lessX); 
	}else{
		sort(P+l,P+r,lessY); 
	} 
	//t结点记录点的位置 
	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] = Point(i,x,y);
		T[i].l=T[i].r=T[i].p=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++){
			ans[j].print();
		}
		printf("\n");
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值