范围搜索
原题链接: AOJ_DSL_2_C
-
题目大意
输入二维点集,从点集中输出指定区域内的所有点(包括边界)
-
输入
n
x0 y0
x1 y1
... ...
xn-1 yn-1
q
sx0 tx0 sy0 ty0
sx1 tx1 sy1 ty1
... ...
sxq-1 txq-1 syq-1 tyq-1
n表示点集内点的数量,n不超过500 000
接下来的n行输入每个点的横纵坐标,每个坐标均不超过10亿,不小于-10亿
q表示区域数,q不超过20000
接下来的q行表示每个区域的边界:sxi ≤ x ≤ txi,syi ≤ y ≤ tyi
-
输出
对于每个区域, 按编号升序输出点集中满足条件的点的编号,每个编号占一行,每个区域输出后空一行,没有满足条件的点则输出空行,每个区域不会超过100个点
-
样例输入
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
42
3
5
-
解题思路
将二维的点集转化为一维的点集,创建二叉搜索树,即将所有的点按照二叉搜索树的特点排列在数组中,所有点在数组中的索引证号为该二叉搜索树中序遍历的顺序,这个索引将用于父节点连接子节点,而IOid表示输入的顺序,仅用于输入输出。
构建二叉树时,排序、递归的作用体现在具体图像上是分割区域,反复以x和y为基准分割区域,直到把所有的点都添加到二叉树内,为后续的搜索做准备
搜索开始于根节点,如果当前节点满足条件则添加到结果集,同时还要进行对子树的搜索,如果当前节点不满足条件,则判断当前坐标在当前搜索区间的位置,并根据情况对其子树进行搜索,这里如果不做判断,搜索全部节点的话,结果虽然正确,但是会导致超时。
-
代码与注释
#include <cstdio>
#include <algorithm>
#include <vector>
#define NMAX 500005
#define NUL -1
using namespace std;
int n;
vector<int> result;
struct Node {
int IOid, x, y, left, right;// id、二叉树位置、坐标、子节点
}nodes[NMAX];
/* 测试使用, 输出全部节点信息
void test(){
printf("======================\n");
for(int i=0;i<n;i++){
printf("IOid:%d id:%d loc:%d (%d, %d) left:%d right:%d\n", nodes[i].IOid, i, nodes[i].location, nodes[i].x, nodes[i].y, nodes[i].left, nodes[i].right);
}
printf("======================\n");
}*/
//比较函数,用于按x、y排序
bool lessByX(const Node &node1, const Node &node2){return node1.x < node2.x;}
bool lessByY(const Node &node1, const Node &node2){return node1.y < node2.y;}
/**构建二叉树*/
int buildTree(int left, int right, int depth){
if (left>=right)
return NUL;//递归至叶节点
int mid = (right+left)>>1;//中值
if (depth++ % 2)//奇数行,以y坐标为基准升序排列
sort(nodes+left, nodes+right, lessByY);
else sort(nodes+left, nodes+right, lessByX);//偶数行
/*测试时用,输出每次排序后的IOid顺序
for(int i =0;i<n;i++){
printf("%d ", nodes[i].IOid);
}
printf("\n");*/
//printf("===loc:%d left:%d right:%d mid:%d\n", loc, left, right, mid);
nodes[mid].left = buildTree(left, mid, depth);//递归左子树
//printf("nodes[%d]左子树返回: %d\n", mid, nodes[mid].left);
nodes[mid].right = buildTree(mid+1, right, depth);//递归右子树
//printf("nodes[%d]右子树返回: %d\n", mid, nodes[mid].right);
return mid;
}
/**范围查询*/
void findNodes (int nowLoc, int xMin, int xMax, int yMin, int yMax, int depth) {
int nowX = nodes[nowLoc].x;
int nowY = nodes[nowLoc].y;
if(nowX >= xMin && nowX <= xMax && nowY <= yMax && nowY >= yMin){
result.push_back(nodes[nowLoc].IOid);//将查询到的节点保存到结果集
// printf("found: nowLoc:%d IOid:%d\n", nowLoc, nodes[nowLoc].IOid);
}
if(depth++ % 2) {//奇数行,比较y
if(nodes[nowLoc].left!=NUL && nowY >= yMin)// y比最小值大,搜索左子树
findNodes(nodes[nowLoc].left, xMin, xMax, yMin, yMax, depth);
if(nodes[nowLoc].right!=NUL && nowY <= yMax)// y比最大值小,搜索右子树
findNodes(nodes[nowLoc].right, xMin, xMax, yMin, yMax, depth);
} else {//偶数行,比较x
if(nodes[nowLoc].left!=NUL && nowX >= xMin)// x比最小值大,搜索左子树
findNodes(nodes[nowLoc].left, xMin, xMax, yMin, yMax, depth);
if(nodes[nowLoc].right!=NUL && nowX <= xMax)// x比最小值大,搜索右子树
findNodes(nodes[nowLoc].right, xMin, xMax, yMin, yMax, depth);
}
}
int main(){
int xMin, xMax, yMin, yMax;
short q;
freopen("test.txt", "r", stdin);// 提交时删除!!!!
freopen("out.txt", "w", stdout);// 提交时删除!!!!
scanf("%d", &n);
for (int i = 0;i<n;i++){
scanf("%d%d", &nodes[i].x, &nodes[i].y);
nodes[i].IOid = i;
}
int root = buildTree(0, n, 0);//构建二叉树
//test();
scanf("%d", &q);
while (q--) {
scanf("%d%d%d%d", &xMin, &xMax, &yMin, &yMax);
findNodes(root, xMin, xMax, yMin, yMax, 0);//从二叉树的根节点执行查询
sort(result.begin(), result.end());//对结果集进行排序
int length = result.size();
for(int i=0;i<length;i++){
printf("%d\n", result[i]);
}
result.clear();//清空结果集
printf("\n");
//test();
}
return 0;
}