【算法竞赛学习笔记】KD-Tree

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

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]=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值