K-D Tree

Posted lhm-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K-D Tree相关的知识,希望对你有一定的参考价值。

(K-D Tree)是一种可以高效处理(k)维空间信息的数据结构

(K-D Tree)具有二叉搜索树的形态,二叉搜索树上的每个结点都对应(k)维空间内的一个点。其每个子树中的点都在一个(k)维的超长方体内,这个超长方体内的所有点也都在这个子树中

同时用(ma)(mi)来记录划分出的子空间的边界

建树的过程是不断划分(k)维空间的过程

(K-D Tree)上,第(i)层中的节点的关键字为节点所对应的空间中的点的第(i mod k)维坐标

以二维举例

第一层在(x)轴上选取中位数作为根,第二层在(y)轴上选取中位数作为根,第三层在(x)轴上选取中位数作为根,以此类推

建树的时间复杂度为(O(n log n))

技术图片

技术图片

技术图片

(K-D Tree)上查询时,若直接遍历整棵树,时间复杂度为(O(n)),所以需进行剪枝,使复杂度降到(O(sqrt n))

K远点对:查询二维平面上欧氏距离下的第(k)远点对

(code:)

struct KD_tree
{
    ll d[2];
    int mi[2],ma[2],ls,rs,id;
}t[maxn],dat[maxn];
bool cmp(const KD_tree &a,const KD_tree &b)
{
    return a.d[type]<b.d[type];
}
void pushup(int cur)
{
    int ls=t[cur].ls,rs=t[cur].rs;
    for(int i=0;i<=1;++i)
    {
        t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
        if(ls)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
        }
        if(rs)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
        }
    }
}
void build(int l,int r,int k,int &cur)
{
    cur=++tot,type=k;
    int mid=(l+r)>>1;
    nth_element(dat+l+1,dat+mid+1,dat+r+1,cmp);
    t[cur]=dat[mid];
    if(l<mid) build(l,mid-1,k^1,t[cur].ls);
    if(r>mid) build(mid+1,r,k^1,t[cur].rs);
    pushup(cur);
}
ll calc(ll x)
{
    return x*x;
}
ll dis(int cur,ll x,ll y)
{
    return calc(t[cur].d[0]-x)+calc(t[cur].d[1]-y);
}
ll dist(int cur,ll x,ll y)
{
    return max(calc(t[cur].ma[0]-x),calc(t[cur].mi[0]-x))+max(calc(t[cur].ma[1]-y),calc(t[cur].mi[1]-y));
}
void query(int cur,ll x,ll y)
{
    ll d,dl,dr;
    int ls=t[cur].ls,rs=t[cur].rs;
    d=dis(cur,x,y);
    if(d>q.top()) q.pop(),q.push(d);
    if(ls) dl=dist(ls,x,y);
    else dl=-inf;
    if(rs) dr=dist(rs,x,y);
    else dr=-inf;
    if(dl>q.top()) query(ls,x,y);
    if(dr>q.top()) query(rs,x,y);
}

......

for(int i=1;i<=2*k;++i) q.push(0);
for(int i=1;i<=n;++i)
    query(root,dat[i].d[0],dat[i].d[1]);

巧克力王国:查询二维平面上满足(ax+by < c)(每次询问给出(a b c))的点的权值和

(code:)

struct KD_tree
{
    int d[2],mi[2],ma[2],ls,rs,id;
    ll val,sum;
}t[maxn],dat[maxn];
bool cmp(const KD_tree &a,const KD_tree &b)
{
    return a.d[type]<b.d[type];
}
void pushup(int cur)
{
    int ls=t[cur].ls,rs=t[cur].rs;
    for(int i=0;i<=1;++i)
    {
        t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
        if(ls)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
        }
        if(rs)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
        }
    }
    t[cur].sum=t[ls].sum+t[rs].sum+t[cur].val;
}
void build(int l,int r,int k,int &cur)
{
    cur=++tot,type=k;
    int mid=(l+r)>>1;
    nth_element(dat+l+1,dat+mid+1,dat+r+1,cmp);
    t[cur]=dat[mid];
    if(l<mid) build(l,mid-1,k^1,t[cur].ls);
    if(r>mid) build(mid+1,r,k^1,t[cur].rs);
    pushup(cur);
}
bool check(ll x,ll y)
{
    return a*x+b*y<c;
}
ll query(int cur)
{
    ll ans=0;
    int ls=t[cur].ls,rs=t[cur].rs,cnt=0;
    cnt+=check(t[cur].ma[0],t[cur].ma[1]);
    cnt+=check(t[cur].ma[0],t[cur].mi[1]);
    cnt+=check(t[cur].mi[0],t[cur].mi[1]);
    cnt+=check(t[cur].mi[0],t[cur].ma[1]);
    if(cnt==4) return t[cur].sum;
    if(!cnt) return 0;
    if(check(t[cur].d[0],t[cur].d[1])) ans+=t[cur].val;
    if(ls) ans+=query(ls);
    if(rs) ans+=query(rs);
    return ans;
}

简单题:支持插入点和查询二维平面上矩形内所有点的权值和,强制在线,用(K-D Tree)实现,当不平衡时,像替罪羊树一样重构

(code:)

struct KD_tree
{
    int d[2],mi[2],ma[2],ls,rs,val,sum,siz;
}t[maxn],dat[maxn],p;
bool cmp(const KD_tree &a,const KD_tree &b)
{
    return a.d[type]<b.d[type];
}
int add()
{
    if(top) return st[top--];
    return ++tot;
}
void pushup(int cur)
{
    int ls=t[cur].ls,rs=t[cur].rs;
    for(int i=0;i<=1;++i)
    {
        t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
        if(ls)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
        }
        if(rs)
        {
            t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
            t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
        }
    }
    t[cur].sum=t[ls].sum+t[rs].sum+t[cur].val;
    t[cur].siz=t[ls].siz+t[rs].siz+1;
}
void build(int l,int r,int k,int &cur)
{
    cur=add(),type=k;
    int mid=(l+r)>>1;
    nth_element(dat+l+1,dat+mid+1,dat+r+1,cmp);
    t[cur]=dat[mid];
    t[cur].ls=t[cur].rs=0;
    if(l<mid) build(l,mid-1,k^1,t[cur].ls);
    if(r>mid) build(mid+1,r,k^1,t[cur].rs);
    pushup(cur);
}
void del(int cur)
{
    if(!cur) return;
    dat[++now]=t[cur];
    st[++top]=cur;
    del(t[cur].ls),del(t[cur].rs);
}
void check(int &cur,int k)
{
    int ls=t[cur].ls,rs=t[cur].rs;
    if(t[cur].siz*alpha<max(t[ls].siz,t[rs].siz))
        now=0,del(cur),build(1,t[cur].siz,k,cur);
}
void insert(KD_tree p,int k,int &cur)
{
    if(!cur)
    {
        cur=add();
        t[cur]=p;
        t[cur].ls=t[cur].rs=0;
        pushup(cur);
        return;
    }
    if(p.d[k]<=t[cur].d[k]) insert(p,k^1,t[cur].ls);
    else insert(p,k^1,t[cur].rs);
    pushup(cur);
    check(cur,k);
}
bool check_p(KD_tree p)
{
    return p.d[0]<=bx&&p.d[0]>=ax&&p.d[1]<=by&&p.d[1]>=ay;
}
bool check_in(KD_tree p)
{
    return p.mi[0]>=ax&&p.ma[0]<=bx&&p.mi[1]>=ay&&p.ma[1]<=by;
}
bool check_out(KD_tree p)
{
    return p.mi[0]>bx||p.ma[0]<ax||p.mi[1]>by||p.ma[1]<ay;
}
int query(int cur)
{
    int ls=t[cur].ls,rs=t[cur].rs,val=t[cur].val,sum=t[cur].sum,ans=0;
    if(check_in(t[cur])) return sum;
    if(check_out(t[cur])) return 0;
    if(check_p(t[cur])) ans+=val;
    if(ls) ans+=query(ls);
    if(rs) ans+=query(rs);
    return ans;
}

以上是关于K-D Tree的主要内容,如果未能解决你的问题,请参考以下文章

K-D Tree

简单题(K-D Tree)

k-d tree模板练习

k-d tree学习总结

BZOJ 2648: SJY摆棋子(K-D Tree)

K-D TREE算法原理及实现