转自:https://zhuanlan.zhihu.com/p/23966698
kd树是一个二叉树结构,它的每一个节点记录了【特征坐标,切分轴,指向左枝的指针,指向右枝的指针】。
其中,特征坐标是线性空间的一个点 。
切分轴由一个整数r表示,这里,是我们在n维空间沿第r维进行一次分割。
节点的左枝和右枝分别都是kd树,并且满足:如果y是左枝的一个特征坐标,那么,并且如果z是右枝的一个特征坐标,那么。
给定一个数据样本集和切分轴r,以下递归算法将构建一个以S为数据集的kd树,每一次循环制作一个节点:
——如果,记录S中唯一的数据为当前节点的特征数据(特征坐标),并且不设左枝和右枝(是指集合S中元素的数量)。
——如果:
--将S内所有的点按第r个坐标的大小进行排序 ;
--选出该排序后的中位元素(如果一共有偶数个元素,则选择中位左边或者中位右边的元素,左或右并无影响),作为当前节点的特征坐标,并且记录切分轴r;
--将设为在S中排在中位元素之前的元素;设为在S中所有排列在中位元素后的元素;
--当前节点的左枝设为以为数据集并且r为切分轴制作出的kd树,当前节点的右枝设为以为数据集并且r为切分轴制作出的kd树。再设(这里,我们想轮流沿着每个维度进行分割;是因为一共有n个维度,在沿着最后一个维度进行分割之后再重新回到第一个维度)。
kd 树上的 kNN 算法
给定一个构建于一个样本集的 kd 树,下面的算法可以寻找距离某个点 p 最近的 k 个样本。
零、设 L 为一个有 k 个空位的列表,用于保存已搜寻到的最近点。
一、根据 p 的坐标值和每个节点的切分向下搜索(也就是说,如果树的节点是照 进行切分,并且 p 的 r 坐标小于 a,则向左枝进行搜索;反之则走右枝)。
二、当达到一个底部节点时,将其标记为访问过。如果 L 里不足 k 个点,则将当前节点的特征坐标加入 L ;如果 L 不为空并且当前节点的特征与 p 的距离小于 L 里最长的距离,则用当前特征替换掉 L 中离 p 最远的点。
三、如果当前节点不是整棵树最顶端节点,执行 (a);反之,输出 L,算法完成。
a. 向上爬一个节点。如果当前(向上爬之后的)节点未曾被访问过,将其标记为被访问过,然后执行 (1) 和 (2);如果当前节点被访问过,再次执行 (a)。
1. 如果此时 L 里不足 kk 个点,则将节点特征加入 L;如果 L 中已满 k 个点,且当前节点与 p 的距离小于 L 里最长的距离,则用节点特征替换掉 L 中离最远的点。
2. 计算 p 和当前节点切分线的距离。如果该距离大于等于 L 中距离 p 最远的距离并且 L 中已有 k 个点,则在切分线另一边不会有更近的点,执行 (三);如果该距离小于 L 中最远的距离或者 L 中不足 k 个点,则切分线另一边可能有更近的点,因此在当前节点的另一个枝从 (一) 开始执行。