KD-Tree

yyb简单的学习KD-Tree

感觉最近一直在学东西来着。

至于原因?

还是我太弱了,每次看一道题:啥?这是什么?根本没学过啊。

索性这段时间就多学点新知识吧。

说起来,我现在最近根本不会取标题名字了,就随意一点吧。

还是这样一个一个的\(part\)写起来比较舒服。

KD-Tree是什么?

我们先来理解一下\(K\)\(D\)\(Tree\)

首先\(Tree\)是什么?你自己回答吧。

然后\(K\)是什么?\(K\)当然就是\(K\)啊,还是什么?

\(D\)?似乎是\(demensional\)??也就是维度的意思

这样就能解释为啥\(K\)就是\(K\)

所以翻译过来就是“K维树”咯。

因此,\(KD-Tree\)用来分割\(K\)维空间,然后可以在上面搞各种各样的事情?

似乎就是这样的。

怎么分割空间啊?

显然的,在我们的空间中,有一堆的点。

我们现在要处理他们。

所以我们选择任意一个维度排序

然后选择中间那个点的这一维来分割空间。

按照这一维排序后左右就分成了两部分。

这样子就可以分割空间了。(有一个\(STL\)的函数叫做\(nth\_element\),可以了解一下)

(听说维度不能那么随意的选择,比如你一直只按照一个维度划分似乎就不是很好?)

(网上的博客主要是推荐按照每个维度依次分割,如果所有维度都分割完了就再从第一个开始循环)

(似乎还有一种更好的方法?算下方差什么的,反正我是不打算用这种啦)

怎么算答案啊

我们现在已经把空间给分割,对于每一块分割出来的空间,里面有若干个点。

我们对于这些点维护一些信息,在回答询问的时候,利用这些信息估算一下答案,

如果最有情况下的答案不优于当前答案,我们就不在这个空间内计算答案。

听着很有道理?

怎么越听越觉得像剪枝?

恩,好像就是剪枝诶!

所以我们的\(KD-Tree\)也是一个暴力?

反正我是这么认为的。

怎么写啊

首先我们需要一个\(Build\)函数来构建\(KD-Tree\)

于是它大概长成这个样子

int Build(int l,int r,int nD)
{
    int x=(l+r)>>1;D=nD;
    nth_element(&a[l],&a[x],&a[r+1]);
    t[x].d[0]=t[x].mn[0]=t[x].mx[0]=a[x].d[0];
    t[x].d[1]=t[x].mn[1]=t[x].mx[1]=a[x].d[1];
    if(l<x)ls=Build(l,x-1,nD^1),update(x,ls);
    if(r>x)rs=Build(x+1,r,nD^1),update(x,rs);
    return x;
}

对于插入操作,我们模仿\(Build\)的这个递归操作,

对于当前节点所掌控的空间进行当前维度的比较,检查应该分像左侧还是右侧。

等等?这听起来怎么这么像\(SBT\)??反正我也是这么觉得的

也许\(KD-Tree\)就是\(SBT\)在空间上的拓展???

话说回来,\(KD-Tree\)在处理实际问题的时候,不同的剪枝函数要自己仔细思考,这样才能够更快。

听说有的时候因为插点插得太多了,导致\(KD-Tree\)变得非常的不平衡,

我们可以类似替罪羊树一样把它推倒重建。

对于每个节点我们额外维护一个\(size\)

每次\(check\)一下当前节点左右子树的\(size\)的比值时候超过了设定的\(alpha\)

如果超过了直接拍扁重建一次就好啦

qaq,蜜汁不难???

posted @ 2018-05-21 19:12 小蒟蒻yyb 阅读(...) 评论(...) 编辑 收藏