作用
一种用于储存多维数据的数据结构,同时可以查询多维空间中距离它最近的一个点.
做法
build
首先它是一棵二叉树,而每一层用不同维进行分组,标准写法是每次求出每一维的方差,选取方差最大的那一维作为比较依据(后面与二叉查找树相同),找到这一维的中位数,分成两组,然后递归建树,复杂度为O(n*(logn)^2).
但实际上可以rand()一维或者直接为(其父节点的维数%维数+1),虽然后面的操作中这不是最优的,但是在建树时复杂度仅为O(n*logn).
其中查找中位数并分成小于它和大于它的两堆时可以用系统中的函数nth_element()来完成。
find(查找最近点)
可以发现K-D Tree本质上是将在多维中的点,划为较小维空间中的点,在二维中的表现就是将每个点分到一个个矩形中,我们可以求出所有点在其子树中每一维的最大值与最小值,这样每个点都可以看作一个矩形(二维平面中)。
与二叉查找树相似,首先查找点计算到当前点距离,根据当前点的比较方式像加入这个点一样向下找,如果查找点与当前点代表的矩形或多维空间的最近距离小于此时的最优答案,也就是这个点的最近点有可能出现在另一棵子树时,则要向另外一棵子树查找,其复杂度平均为O(n*logn),最劣为O(n *sqrt(n)).
查找最远点时大同小异,复杂度相同。
代码(平面上最近点对)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define ll long long
#define db double
#define N 100100
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
ll n,k,nw,mx,rt,ans,an=INF;
struct Dn
{
ll num[2];
bool operator < (const Dn &u) const
{
return num[nw]<u.num[nw];
}
} tmp[N],t,yl[N];
struct Node
{
ll ls,rs,mn[2],mx[2],ws;
Dn it;
} node[N];
inline void Min(ll &u,ll v)
{
if(v<u) u=v;
}
inline void Max(ll &u,ll v)
{
if(v>u) u=v;
}
inline void up(ll now)
{
ll i,L=node[now].ls,R=node[now].rs;
for(i=0; i<2; i++)
{
node[now].mn[i]=node[now].mx[i]=node[now].it.num[i];
if(L) Min(node[now].mn[i],node[L].mn[i]),Max(node[now].mx[i],node[L].mx[i]);
if(R) Min(node[now].mn[i],node[R].mn[i]),Max(node[now].mx[i],node[R].mx[i]);
}
}
ll build(ll l,ll r)
{
if(l>r) return 0;
ll now=++mx,mid=((l+r)>>1);
nw=node[now].ws=rand()%2;
nth_element(tmp+l,tmp+mid,tmp+r+1);
node[now].it=tmp[mid];
node[now].ls=build(l,mid-1);
node[now].rs=build(mid+1,r);
up(now);
return now;
}
inline ll dis(ll u)
{
return (node[u].it.num[0]-t.num[0])*(node[u].it.num[0]-t.num[0])+(node[u].it.num[1]-t.num[1])*(node[u].it.num[1]-t.num[1]);
}
inline void len(ll now)
{
ll res=dis(now);
if(res) Min(ans,res);
}
inline ll gd(ll now)
{
ll res=0,i,t2;
for(i=0; i<2; i++)
t2=max(0ll,node[now].mn[i]-t.num[i])+max(0ll,t.num[i]-node[now].mx[i]),res+=t2*t2;
return res;
}
void ask(ll now)
{
if(!now) return;
nw=node[now].ws;
if(t<node[now].it)
{
len(now);
ask(node[now].ls);
if(gd(node[now].rs)<ans)
ask(node[now].rs);
}
else
{
len(now);
ask(node[now].rs);
if(gd(node[now].ls)<ans)
ask(node[now].ls);
}
}
int main()
{
srand(517);
ll i,j;
mx=0;
scanf("%lld",&n);
for(i=1; i<=n; i++) scanf("%lld%lld",&tmp[i].num[0],&tmp[i].num[1]);
rt=build(1,n);
for(i=1; i<=n; i++)
{
t=tmp[i];
ans=INF;
ask(rt);
an=min(an,ans);
}
printf("%.4f\n",sqrt((db)an));
}