学习笔记第五十四节:K-D Tree

本文深入探讨了K-D树这一高效的数据结构,详细介绍了其在高维空间中的应用,如三维最近点对查找等。通过二叉搜索树的形式,K-D树能够循环地按不同维度进行排序,实现对空间信息的有效查询与剪枝优化。文章提供了具体实例和简洁的代码实现,展示了K-D树在实际问题解决中的强大能力。

正题

      翻译过来就是 k维的树,很显然就可以看出它是一个高维数据结构。

      因为它高维,所以它的局限性就比线段树要小很多。

      一般可以做k维空间的信息,比如三维最近点对,之类的。

      它使用一个二叉搜索树来维护的,每一维排序的关键字是循环的,比如说第一层按照第一维来排,第二层按照第二维来拍,...,第k层按照第k维来排,第(k+1)层按照第1维来排。

      然后访问空间信息的时候就可以在上面做一些剪枝优化,一般的,一颗维护K维空间的KDT每次查询的时间是n^{\frac{k-1}{k}}

      然后代码也十分简短:

      以[国家集训队]JZPFAR为例,注意打kdt查询的时候尽量把能加上的优化都加上,尽管不是那么显然的优化,有时候10->100就靠它了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,rt,num=0,m;
int sx,sy;

struct node{
	int x,y,id;
}s[N];
struct Heap{
	int x;
	long long dis;
	bool operator<(const Heap q)const{
		return dis!=q.dis?dis>q.dis:x<q.x;
	}
};
priority_queue<Heap> q;
struct KDT{
	int x0,y0,x1,y1;
	node p;
	int ls,rs;
}T[N];

long long get_dis(node a){return 1ll*(sx-a.x)*(sx-a.x)+1ll*(sy-a.y)*(sy-a.y);}
bool cmp1(node x,node y){return x.x<y.x;}
bool cmp2(node x,node y){return x.y<y.y;}

void read(int&x){
	char ch=getchar();x=0;
	bool tf=false;
	while(ch<'0' || ch>'9') ch=='-'?tf=true:0,ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	tf?x=-x:0;
}

int build(int l,int r,int type){
	int xmin=1e9,ymin=1e9,xmax=-1e9,ymax=-1e9;
	for(int i=l;i<=r;i++){
		xmin=min(xmin,s[i].x),xmax=max(xmax,s[i].x);
		ymin=min(ymin,s[i].y),ymax=max(ymax,s[i].y);
	}
	int mid=(l+r)/2,now=++num;
	nth_element(s+1+l,s+1+mid,s+1+r,type?cmp1:cmp2);
	T[now]=(KDT){xmin,ymin,xmax,ymax,s[mid],0,0};
	if(l<mid) T[now].ls=build(l,mid-1,type^1); 
	if(mid<r) T[now].rs=build(mid+1,r,type^1);
	return now;
}

long long max_dis(int now){
	if(!now) return -10;
	return max(max(get_dis((node){T[now].x0,T[now].y0}),get_dis((node){T[now].x0,T[now].y1}))
	,max(get_dis((node){T[now].x1,T[now].y0}),get_dis((node){T[now].x1,T[now].y1})));
}

void query(int now){
	long long res=get_dis(T[now].p);
	if(res>q.top().dis || (res==q.top().dis && T[now].p.id<q.top().x))
		q.pop(),q.push((Heap){T[now].p.id,res});
	long long D1=max_dis(T[now].ls),D2=max_dis(T[now].rs);
	if(D1>D2){
		if(D1>=q.top().dis) query(T[now].ls);
		if(D2>=q.top().dis) query(T[now].rs);
	}
	else{
		if(D2>=q.top().dis) query(T[now].rs);
		if(D1>=q.top().dis) query(T[now].ls);
	}
}

int main(){
	read(n);
	int x,y,k;
	for(int i=1;i<=n;i++) read(s[i].x),read(s[i].y),s[i].id=i;
	rt=build(1,n,0);
	read(m);
	while(m--){
		read(x);read(y);read(k);sx=x,sy=y;k=abs(k);
		while(!q.empty()) q.pop();
		for(int i=1;i<=k;i++) q.push((Heap){-1,-1});
		query(rt);
		printf("%d\n",q.top().x);
	}
} 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值