从拥有多个属性的报表集合(数据库)中,寻找具有特定属性且位于指定范围内的元素,这类问题称为范围搜索。
编写程序,对魔某个二维平面上点的集合,列举出给定范围内的点。另外,给定的点集合无法进行点的添加和删除操作(静态)。
输入
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}绘制成二叉树。
树节点号 | 3 | 2 | 1 | 5 | 4 | 0 | 8 | 7 | 6 | 9 |
编号/数值 | 0/1 | 1/3 | 2/5 | 3/6 | 4/10 | 5/13 | 6/14 | 7/16 | 8/19 | 9/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;
}