K-D树

定义与原理

  • 定义:K-D 树是一种二叉树,它是对数据点在 K 维空间中进行划分的树形数据结构。每个节点表示一个 K 维空间中的超平面,将空间划分为两个子空间,每个子节点对应一个子空间。
  • 原理:K-D 树的构建基于数据点在各个维度上的取值。在构建过程中,会选择一个维度作为划分维度,然后根据该维度上的数据值将数据集划分为左右两个子集,分别作为当前节点的左右子树。通常选择数据在该维度上的中位数作为划分点,这样可以使树的结构更加平衡,提高查询效率。

构建过程

  • 选择划分维度:可以采用轮询法,即从第 1 维开始,依次选择每个维度作为划分维度,循环进行。也可以根据数据在各个维度上的方差来选择,方差越大,说明数据在该维度上的分布越分散,选择该维度作为划分维度能更好地将数据分开。
  • 确定划分点:在选定的划分维度上,找到数据集中所有点在该维度上的中位数,将其作为划分点。
    划分数据集:根据划分点,将数据集分为左右两个子集,左子集中的点在划分维度上的值小于等于划分点,右子集中的点在划分维度上的值大于划分点。
  • 递归构建子树:对左右两个子集分别递归地执行上述步骤,直到子集中的点数量小于等于某个阈值或者所有点在所有维度上的值都相同,此时构建出一棵 K-D 树。

常见操作

  • 插入操作:插入一个新的数据点时,从根节点开始,根据当前节点的划分维度和划分点,判断新点应该进入左子树还是右子树,然后递归地在相应的子树中进行插入操作,直到找到合适的叶子节点位置插入新点。
  • 搜索操作:给定一个目标点和一个搜索半径,从根节点开始,计算目标点与当前节点的距离,判断当前节点是否在搜索范围内。如果在范围内,则将当前节点加入结果集。然后根据目标点与划分超平面的位置关系,决定先搜索左子树还是右子树。如果目标点到划分超平面的距离小于搜索半径,则需要搜索另一侧的子树,直到遍历完所有可能包含目标点的子树。
  • 删除操作:删除一个点时,先找到要删除的点所在的叶子节点。如果该节点是叶子节点,直接删除。如果不是叶子节点,则需要进行调整,通常是找到该节点的中序后继节点,将后继节点的值赋给当前节点,然后删除后继节点,再通过旋转等操作来维护 K-D 树的结构。

板子

#include <bits/stdc++.h>
using namespace std;
const int N = 3, M = 1e4 + 5;
int K;
int root,cur;//根节点,当前点
struct node
{
	int l, r;
	double v[N];
	double U[N], L[N]; // 最右边(大,最左边(小
	bool operator<(const node &b) const
	{
		return v[K] < b.v[K];
	}
} tr[N];



void pushup(int mid) // 处理中间点的坐标范围
{
	for (int i = 0; i < N; i++) // 每个维度
	{
		tr[mid].L[i] = tr[mid].U[i] = tr[mid].v[i]; // 初始为自己坐标
		if (tr[mid].l)
			tr[mid].L[i] = min(tr[mid].L[i], tr[tr[mid].l].L[i]),
			tr[mid].U[i] = max(tr[mid].U[i], tr[tr[mid].l].U[i]);
		if (tr[mid].r)
			tr[mid].L[i] = min(tr[mid].L[i], tr[tr[mid].r].L[i]),
			tr[mid].U[i] = max(tr[mid].U[i], tr[tr[mid].r].U[i]);
	}
}
int build(int l, int r, int k)
{
	if (l > r)
		return 0;
	int mid = (l + r) / 2;
	K = k;									   // k维度
	nth_element(tr + l, tr + mid, tr + r + 1); // 得到中点值
	tr[mid].l = build(l, mid - 1, k ^ 1);
	tr[mid].r = build(mid + 1, l, k ^ 1);
	pushup(mid);
	return mid;
}


double sqrt(double x)
{
	return x*x;
}
double dis(int p)	//当前点到p的距离
{
	double s=0;
	for(int i=0;i<N;i++)
		s+=sqrt(tr[cur].v[i]-tr[p].v[i]);
	return s;
}
double dis2(int p)
{
	if(!p)
		return 1e18;
	int s=0;
	for(int i=0;i<N;i++)
		s+=sqrt(max(tr[cur].v[i]-tr[p].U[i],0.0)+max(tr[p].L[i]-tr[cur].v[i],0.0));
	return s;
}
double ans;
void query(int p)//查询到当前点距离最小的距离
{
	if(!p)	return ;
	if(p!=cur)	ans=min(ans,dis(p));
	double dl=dis2(tr[p].l),dr=dis2(tr[p].r);
	if(dl<dr)//优先查询预计答案更小的
	{
		if(dl<ans)	query(tr[p].l);//如果dl>=ans就没有更新意义
		if(dr<ans)	query(tr[p].r);
	}
	else
	{
		if(dr<ans)	query(tr[p].r);
		if(dl<ans)	query(tr[p].l);
	}
}



int main()
{

	return 0;
}
posted @ 2025-02-25 07:42  流氓兔LMT  阅读(58)  评论(0)    收藏  举报