k-d树

Posted

tags:

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

1.1、构建k-d树

      k-d树是K-dimension tree的缩写,是对数据点在k维空间(如二维(x,y),三维(x,y,z),k维(x,y,z,...))中划分的一种数据结构,主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。本质上说,k-d树就是一种平衡二叉树。

      首先必须搞清楚的是,k-d树是一种空间划分树,说白了,就是把整个空间划分为特定的几个部分,然后在特定空间的部分内进行相关搜索操作。想像一个三维空间,k-d树按照一定的划分规则把这个三维空间划分了多个空间,如下图所示:

技术分享

1.2、构建k-d树伪代码

算法:构建k-d树(createKDTree)
输入:数据点集:Data-set和其所在的空间:Range
输出:kd,类型为k-d tree


1、If data-set为空,则返回空的k-d tree


2、调用节点生成程序:
1)确定split域:对于所有描述子数据(特征矢量),统计它们在每个维上的数据方差。以SURF为例,描述子为64维,可计算64个方差。挑选出方差中的最大值,对应的维就是split域的值。数据方差大表明沿该坐标轴方向上的数据分散得比较开,在这个方向上进行数据分割有较好的分辨率;
2)确定Node-data域:数据点集Data-set按其第split域的值排序。位于正中间的那个数据点被选为Node-data。此时新的Data-set‘ = Data-set \ Node-data(除去其中Node-data这一点)。

 

3、dataleft = {d属于Data-set‘ && d[split] ≤ Node-data[split]}
      Left_Range = {Range && dataleft}
      dataright = {d属于Data-set‘ && d[split] > Node-data[split]}
      Right_Range = {Range && dataright}

 

4、left = 由(dataleft,Left_Range)建立的k-d tree,即递归调用createKDTree(dataleft,Left_Range)

     设置:left的parent域(父节点)为Kd;
     right = 由(dataright,Right_Range)建立的k-d tree,即调用createKDTree(dataleft,Left_Range)

     设置:right的parent域为Kd。

 

1.3、构建k-d树例子

      举一个简单直观的实例来介绍k-d树构建算法。假设有6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},数据点位于二维空间内,如下图所示。

 

 

6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}构建kd树的具体步骤为:

1)确定split域=x。具体是:6个数据点在x,y维度上的数据方差分别为39,28.63,所以在x轴上方差更大,故split域值为x;

2)确定Node-data = (7,2)。具体是:根据x维上的值将数据排序,6个数据的中值(所谓中值,即中间大小的值)为7,所以Node-data域位数据点(7,2)。这样,该节点的分割超平面就是通过(7,2)并垂直于:split=x轴的直线x=7;

3)确定左子空间和右子空间。具体是:分割超平面x=7将整个空间分为两部分:x<=7的部分为左子空间,包含3个节点={(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点={(9,6),(8,1)};

如上算法所述,kd树的构建是一个递归过程,我们对左子空间和右子空间内的数据重复根节点的过程就可以得到一级子节点(5,4)和(9,6),同时将空间和数据集进一步细分,如此往复直到空间中只包含一个数据点。
技术分享

      与此同时,经过对上面所示的空间划分之后,我们可以看出,点(7,2)可以为根结点,从根结点出发的两条红粗斜线指向的(5,4)和(9,6)则为根结点的左右子结点,而(2,3),(4,7)则为(5,4)的左右孩子(通过两条细红斜线相连),最后,(8,1)为(9,6)的左孩子(通过细红斜线相连)。如此,便形成了下面这样一棵k-d树:

技术分享

1.4、k-d树数据结构

技术分享

      针对上表给出的kd树的数据结构,转化成具体代码如下所示(注,以下代码分析基于Rob Hess维护的sift库)

技术分享

      如之前所述,kd树中,kd代表k-dimension,每个节点即为一个k维的点。每个非叶节点可以想象为一个分割超平面,用垂直于坐标轴的超平面将空间分为两个部分,这样递归的从根节点不停的划分,直到没有实例为止。经典的构造k-d tree的规则如下:
1)随着树的深度增加,循环的选取坐标轴,作为分割超平面的法向量。例如对于3-d tree来说,根节点选取x轴,根节点的孩子选取y轴,根节点的孙子选取z轴,根节点的曾孙子选取x轴,这样循环下去。

2)每次均为所有对应实例的中位数的实例作为切分点,切分点作为父节点,左右两侧为划分的作为左右两子树。

技术分享

      对于n个实例的k维数据来说,建立kd-tree的时间复杂度为O(k*n*logn)

 

1.5、构建k-d树的代码

技术分享

上面的涉及初始化操作的两个函数kd_node_init,及expand_kd_node_subtree代码分别如下所示:

技术分享

技术分享

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

如何从 k-d 树实现 K-NN 分类?

k-d tree

最近邻居 - k-d 树 - ***证明

空间划分的数据结构(四叉树/八叉树/BVH树/BSP树/k-d树)

bzoj 2716 天使玩偶 —— K-D树

2016 ICPC青岛站---k题 Finding Hotels(K-D树)