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,蜜汁不难???