K-D Tree

54 篇文章 0 订阅
21 篇文章 0 订阅

作用

一种用于储存多维数据的数据结构,同时可以查询多维空间中距离它最近的一个点.

做法

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));
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值