备注:本笔记只用作学习记录。

一、参考文献

kd 树算法之思路篇
kd 树算法之详细篇

二、二叉树

kdtree
给定一堆已有的样本数据,寻找离五角星最近的15个点——如图,只对紫圈里的点进行计算。

因为计算机没有距离概念,所以要将空间分割成小块,并以合理地方法将信息进行储存,方便计算机读取“附近”的点。

kdtree
如果两个点在树中的距离较近,那么它们的实际距离就是比较近的。

1、案例一

通过这棵二叉树找到离爱心最近的纹身

  • 首先从树的最顶端开始,向下搜寻找到最底部包含爱心的节点
    kdtree
  • 在找到了爱心之后,沿着相同的路径向上攀爬
    发现爱心和月亮在同一个区域,再继续向上爬两个节点,都没有出现爱心和月亮以外的纹身。
    kdtree
    因为爱心和月亮之间的距离(红线)要小于爱心和分割线的距离(蓝线),所以分割线的右边不用再讨论,因此离爱心最近的图形是月亮。

2、案例二

通过这棵二叉树找到离爱心最近的纹身。
kdtree
kdtree
(红线)大于爱心和分割线的距离(蓝线),所以分割线的另一边可能有更近的点,要从另一个分枝开始向下搜

kdtree
圆圈与爱心的距离 > 爱心与月亮的距离,所以丢弃不要,再向上爬一个节。
kdtree
红线大于蓝线,因此切分线的另一端可能有更近的纹身,要从另一个树枝向下搜索。
kdtree
叶子和爱心之间的距离 < 爱心与月亮的距离,再向上攀登一节。
kdtree
红线小于蓝线,因此切分线另一边的数据就不用考虑了。

这次我们已经爬到了树的最顶端,完成了搜索,叶子距离爱心最近。

kdtree

3、总结

找到了比切分线更近的点时,就不需要继续搜索切分线另一边的点了。通过把整个空间进行分割并以树状结构进行记录,我们只需要在问题点附近的一些区域进行搜寻便可以找到最近的数据点,节省了大量的计算。

三、kdtree

1、kdtree的构建

首先随机随机生成 13 个点作为数据集,起始的切分轴 r=0(这里 r=0 对应 x 轴,r=1 对应 y 轴)。

kdtree
我们选出 x 坐标的中位点,获取最根部节点的坐标,并且按照该点的 x 坐标将空间进行切分,构建左枝和右枝。

kdtree
kdtree
再按照 y 轴进行切分,左右两边再按照 y 轴的排序进行切分,中位点记载于左右枝的节点。

kdtree
kdtree
再按照 x 坐标进行排序和切分

kdtree
kdtree
最后每一部分都只剩一个点,将他们记在最底部的节点中。因为不再有未被记录的点,所以不再进行切分,完成了 kdtree的构造。

kdtree

2、案例一

我们想找距离点 p=(−1,−5) 最近的 3 个点。

  • 按照切分找到最底部节点

kdtree
p 的 x 轴更小,因此我们向左枝进行搜索:

kdtree
p 的 y 值更小,因此向左枝进行搜索:

kdtree
这个节点只有一个子枝,就不需要对比了,由此找到了最底部的节点。

kdtree

  • 访问过的节点,在二叉树上划掉。

记录 L=[(−4.6,−10.55)] 。

kdtree
kdtree
记录 L=[(−4.6,−10.55),(−6.88,−5.4),(1.24,−2.86)] 。

当前节点有其他的分枝,经计算得出 p 点和 L 中的三个点的距离分别是6.62,5.89,3.10,但是 p 和当前节点的分割线的距离只有 2.14,小于与 L 的最小距离,因此在分割线的另一端可能有更近的点。

kdtree
p 的 x 坐标更大,因此探索右枝 。

kdtree
经计算,(1.75,12.26) 与 p 的距离是 17.48,大于 p 与 L 的距离,因此不记录。

kdtree
算出该节点与 p 的距离是 4.91,小于 p 与 L 的最大距离 6.62,所以记录 L=[(−2.96,−0.50),(−6.88,−5.4),(1.24,−2.86)] 。

因为有分支,所以计算 p 和当前节点的分割线的距离,小于 L 与 p 的最小距离,因此要到当前节点的另一个枝。

kdtree
计算距离发现这个点离 p 比 L 更远,因此不进行替代。

发现不是顶点,所以继续向上爬。

kdtree
kdtree
kdtree
我们进行计算比对发现顶端节点与 p 的距离比 L 还要更远,因此不进行更新。

计算 p 和分割线的距离发现也是更远,因此也不需要检查另一个分枝。

因为当前节点是顶点,因此计算完成!输出距离 p 最近的三个样本是 L=[(−6.88,−5.4),(1.24,−2.86),(−2.96,−2.5)] 。

相关文章: