title : KD-Tree
date : 2022-4-7
tags : ACM,数据结构
author : Linno
K-D tree
K-D树是在k维欧几里得空间中组织点的数据结构。在算法竞赛中,K-D树往往用于在二维平面内的信息检索。具体应用如:多维键值搜索(范围搜索及最邻近搜索)下面我们针对二维平面的KD-Tree进行讲解。
定义
K-D Tree是一颗存储k维信息的二叉树,代表对数据集合的划分,每个结点都对应着一个k维超矩形区域。
①在一维数据情况下,KD-Tree是一颗二叉搜索树。
②二维情况下,我们轮流按照x维和y维对数据进行划分。这是最简单的一种划分方式,使得每次划分都将平面分成不相交的两部分。
建树
对于KD-Tree每个结点的存储信息如下:
struct KDTree{
int ch[2],id; //左右儿子和当前结点的编号
Point p,r1,r2;
//结点表示的点,子树赋改的矩形的左下角,右上角
}
每次新建结点是根据当前划分维度选中位数进行划分,注意nth_elementnth\_elementnth_element函数的使用,是线性时间选择中位数,并将比他小的元素排序到左边,比他大的元素排序到右边的。
int build(int l,int r,int d) {
//KD-Tree建树
if (l>r) return 0;
del=d; //改变划分维度
int mid=(l+r)>>1,at=++ncnt;
nth_element(ps+l,ps+mid,ps+r+1,cmp); //O(n)找到第mid大元素,将比他小的放左边,比他大的放右边
T[at]=Tree(ps[mid],mid);
T[at].ch[0]=build(l,mid-1,d^1),T[at].ch[1]=build(mid+1,r,d^1);
pushup(at);return at;
}
更新
对于KD-Tree每个结点表示的矩形,需要由左右儿子向上更新
void pushup(int rt) {
//更新操作
T[rt].r1.x=min(min(T[ls].r1.x,T[rs].r1.x),T[rt].r1.x);
T[rt].r1.y=min(min(T[ls].r1.y,T[rs].r1.y),T[rt].r1.y);
T[rt].r2.x=max(max(T[ls].r2.x,T[rs].r2.x),T[rt].r2.x);
T[rt].r2.y=max(max(T[ls].r2.y,T[rs].r2.y),T[rt].r2.y);
}
插入
插入是根据当前层比较的维度,较小的插左子树,较大的插右子树,比较简单。
void modify(int rt,Point p){
if(!rt){
T[rt]=Tree(P,++ncnt);return;}
int d=cmp(p,T[rt].p)^1;
del^=1;
modify(T[rt].ch[d],p);
pushup(T[rt]);
}
查询
最近点查询
1.设定答案ans为初始值∞\infty∞
2.将点P从根节点开始,先用根节点代表的点更新答案。由于根节点的左右儿子各表示一个矩形区域,而两个区域都有可能存在距离P最近的点,我们优先选择距离点P最近的矩形递归查询。
3.以P为圆心,ans为半径画圆,如果与之前未递归的矩形相交,则递归下去,否则不可能由更优解。
事实上剪枝+搜素,单次查询均摊复杂度是O(logn)O(logn)O(logn),最坏O(n)O(\sqrt n)O(n)
void query(int rt,Point p) {
//查询操作
if (!rt) return;
node st=node(dis(T[rt].p,p),T[rt].p.id);
if (st<q.top()) q.pop(),q.push(st);
double dis[2]=

本文详细介绍了KD-Tree这一数据结构,特别是在二维平面内的信息检索中的作用。通过定义、建树、更新、插入和查询操作,展示了KD-Tree如何用于最近点查询和K远点对问题的解决。文中还提供了具体的ACM竞赛题目实例,解释了如何利用KD-Tree解决这些问题,包括插入、查询和维护大根堆等关键步骤。
最低0.47元/天 解锁文章
230

被折叠的 条评论
为什么被折叠?



