k-d tree 介绍

原创 2015年07月10日 14:04:36

作为存取高维数据的一种数据结构,k-d tree 在静态查询和插入方面的效率还是很高的。本文在这里对 k-d tree 的内容作一些介绍,可能也会结合自己使用 k-d tree 的一些体验作一些点评。其实,k-d tree 是早在1975年的时候由 Stanford 的 Bentley 提出来的。本文的内容也主要来自于他的两篇最原始的文章 [Ben75] 和 [FBF77] 。

k-d tree 概述 与 插入操作(Insertion)

首先,k-d tree 也是二叉搜索树的一种,与常见的平衡二叉搜索树(BST)不同的是,在 k-d tree 中,每个节点内存储的都是一条记录(record),或者说是多维空间中的一个点,用一个向量来表示。而且在 k-d tree中,这个点也代表了空间中的一个区域。每个节点都有两个子节点,而且两个子节点各自代表的区域是父节点的区域一个划分。

在一维的情形中,每条 record 都是由一个单独的 key 来表示的。因此,对于 k-d tree 中的每个节点,key 值小于或者等于当前节点的 key 值的点就属于左子树,比当前节点 key 值大的就属于右子树。因此,这里的 key 值就成为了一种鉴别器(discriminator)。而在 k 维的情况中,一条 record 是由 k 个 key 值来表示的,这里每一维的 key 值都可以作为 discriminator 来将一个点向某个节点的左右子树来分类。而在 k-d tree 中,discriminator 的选取是和该节点所在的层数有关的,即在根节点处,即第0层,按照第一维的 key 值来进行分类,第一维的 key 值小于等于根节点的第一维的 key 值的属于根节点的左子树,大于根节点的第一维的 key 值的属于根节点的右子树。然后在根节点的左右子节点的位置上,即第一层的位置上,根据第二维的 key 值来区分,以此类推。即第 k 层要比较的 key 值的维数为 D=L mod k+1 。其中L是当前节点所在的层数,其中根节点即为第0层。

按照 k-d tree 的规则依次插入(0,0), (-10, 10), (10, -10), (-40, -20), (-20, 11), (20, 0)这几个点,我们可以得到如下左图所示的 k-d tree,右图是这几个点在平面的示意图。其中蓝线表示该点处是以第一维的 key 值进行区分,红线表示该点处是以第二维的 key 值进行区分。
这里写图片描述

同时我们还可以看出,k-d tree 中每一个节点其实也代表了k维空间中的一个区域(region)。我们以上述几个二维空间中的点为例。根节点 (0,0) 代表的是全平面,即 (-50, -50, 50, 50) 这样一个区域,这里的区域我们用 (xmin,ymin,xmax,ymax) 来表示,因为根节点 (0,0) 是在第一维,即 x 轴出进行区分的,因此它的左子节点就代表了左半平面,右子节点就代表了右半平面。即点 (-10, 10) 代表的是 (-50, -50, 0, 50) 这样一个区域,点 (10, -10) 代表的是 (0, -50, 50, 50) 这样一个区域。以此类推,在点 (-10, 10) 处,因为是第一层,因此按照第二维来区分,所以点 (-40, -20) 的第二维比点 (-10, 10) 小,就在左面;点 (-20, 11) 的第二维比点 (-10, 10) 大,就在右面。而且,左面的点 (-40, -20) 代表的是它的父节点的下半平面,即 (-50, -50, 0, 10) 这样一个区域;右面的点 (-20, 11) 代表的是它的父节点的上半平面,即 (-50, 10, 0, 50) 这样一个区域。

查找操作(Searching)

上面我们介绍了 k-d tree 的原理和插入节点的过程,现在我们介绍下搜索节点的过程。在 k-d tree 中对点进行搜索的方法有很多。包括:(1)对所有维度进行匹配的特定点查询(精确匹配);(2)对部分维度进行匹配的查询;(3)对某个特定的区域内的点进行进行查询;(4)查找与特定点距离最近的几个点。

上面的几种搜索算法都在 [Ben75] 和 [FBF77] 两篇文章中有详细介绍,在这里我们主要介绍(3),也就是我自己用过的区域查询(Region Query)。区域查询的目标是,在 k-d tree 所代表的空间内,如上面例子中提到的二维平面中的 (-50, -50, 50, 50) 这样一个区域,给定一个矩形的区域(即在各个维度上给出这个区域的上下界),如在上面的例子中我们可以给定 (-45, -30, -30, -10) 这样一个区域,查找所有落在这个区域内的点。

区域查找的主要方法如下:从根节点开始,考察该节点的 key 值所代表的点是否在待查找的区域内,如果在待查区域内,就将这个节点放入一个全局的列表中;在这之后,分别考察该节点的左右子节点所代表区域与待查询的区域是否有交集,如果有,就递归地以该子节点作为根节点,进行上述操作,如果没有就返回。在所有递归函数运行完后,我们可以得到一个全局的列表,这个列表里存储的都是落在待查找区域内的点。从上面的阐述中可以看出,查找算法的复杂度与待查的区域大小有很大关系。虽然根据 [LW77] 的结论,最差情况下区域查找的复杂度会达到 O(kN11k),其中 k 是数据点的维度, N 是 k-d tree 内节点的总个数,但 [Ben75, FB74] 的大量仿真都表明在进行超矩形(hyper-rectangular)区域的搜索时,k-d tree 上的区域搜索的表现相当不错(reasonably well)。

下面给一段我自己用 Matlab 写的 Region Query 的代码,可能有助于理解。

%% range query的函数
function findAll = rangeQuery(obj,id,rect)  
% obj是一个k-d tree的对象,id是从某个id开始查起,用于实现递归,rect是待查区间
    left = obj.nodeCell{id}.leftID;
    right = obj.nodeCell{id}.rightID;
    findLeft = [];
    findRight = [];

    if (isempty(left)==0) && (interRegion(obj.nodeCell{left}.region,rect,obj.dimen)==1)  
    % 左非空,且左有子集,向左搜索
        findLeft = rangeQuery(obj,left,rect);
    end

    if (isempty(right)==0) && (interRegion(obj.nodeCell{right}.region,rect,obj.dimen)==1)  
    % 右非空,且右有子集,向右搜索
    findRight = rangeQuery(obj,right,rect);
    end

    if interPoint(rect,obj.nodeCell{id}.point,obj.dimen)==1         
    % 当前点在range内
        findAll = [id,findLeft,findRight];
    else
        findAll = [findLeft,findRight];
    end

end

其中,interRegion 是一个可以判断两个区域是否相交的函数。

删除操作(Deletion)

其实,k-d tree 对删除操作的支持并不很好,因为 k-d tree 本身不具备平衡性,动态进行的插入和删除操作可能使得 k-d tree 退化成一个线性表。实际上也有关于平衡 k-d tree 的研究,如 [Rob81]。但可能是因为实现起来太复杂的原因,K-D-B tree 似乎没有得到很多应用。

下面我们主要讲一下删除操作,对 k-d tree 内的节点进行删除的原则是,对于一个没有后继结点的外部节点,删除操作可以直接进行;对于有后继结点的内部节点 P,要做的就是从它的子节点中找到一个合适的节点 Q 来放置到这个需要被删除的节点的位置上。而所谓合适的节点,就是说如果 P 节点是在第 J 个维度上进行分界的,那么 Q 就是 P 的左子树中 J 维上最大的节点,或者是 P 的右子树中 J 维上最小的节点,二者均可以。要将 Q 节点替换到 P 节点的位置上去,需要先将 Q 节点从它原来的位置上删除,因此上面所述的删除操作也是一个递归实现的过程。

我自己写的删除操作因为要结合自己其他的应用,因此写得很冗长,就不在这里放出来了。

优化操作(Optimize)

优化操作是 k-d tree 的一种离线操作。我们都知道,当二叉树随着插入操作的进行,如果无法保证树的平衡性,那么在二叉树上进行操作的复杂度会逐渐变差,极端情况下二叉树会退化成为一个线性表。针对一个不平衡的 k-d tree,可以通过优化的操作来使其恢复平衡,以保证后续查找操作的效率。

所谓优化操作,其实就是按照维度的次序,分别将节点进行排序。比如,对于一个需要优化的 k-d tree,对其所有的节点按照第一维度元素进行升序排序,然后最中间的一个作为根节点,然后左半部分的节点作为主子树的节点,有半部分的节点作为又子树的节点。然后分别对左右子树的节点进行上述的处理,只不过参考的维度分别为第二维,第三维……

经过上面的处理,可以使得一个任意的 k-d tree 成为平衡的 k-d tree。

在这里我们对 k-d tree 的内容进行一个小结,针对已有的 N 个数据点,每个点由一个 k 维的数据表征,建立一个 k-d tree 的复杂度为 O(NlogN),对已有的 k-d tree 进行优化的复杂度为 O(NlogN),插入一个节点的复杂度为 O(logN),删除一个节点的复杂度为 O(logN),,进行精确匹配的复杂度为 O(logN) ,查找一个特定的区域的最差情况的复杂度为 O(kN11k),但区域查找的复杂度与区域大小有关,而且平均意义下的效果不错。

Ref.
[Ben75] Bentley, J. L. (1975). Multidimensional binary search trees used for associative searching. Communications of the ACM, 18(9), 509-517.
[FBF77] Friedman, J. H., Bentley, J. L., & Finkel, R. A. (1977). An algorithm for finding best matches in logarithmic expected time. ACM Transactions on Mathematical Software (TOMS), 3(3), 209-226.
[LW77] Lee, D. T., & Wong, C. K. (1977). Worst-case analysis for region and partial region searches in multidimensional binary search trees and balanced quad trees. Acta Informatica, 9(1), 23-29.
[Rob81] Robinson, J. T. (1981, April). The KDB-tree: a search structure for large multidimensional dynamic indexes. In Proceedings of the 1981 ACM SIGMOD international conference on Management of data (pp. 10-18). ACM.

版权声明:本文为博主原创文章,未经博主允许不得转载。

关于B-K tree与k-d tree一些自己想法

以前用B-K tree做过一个题,最近学习k-d tree,感觉这两个树有某些相似,这个俩种树都是寻找距目标点临近最近的点(B-K tree针对给定距离k以内,k-d tree针对给定最近的k个点)。...
  • gyarenas
  • gyarenas
  • 2015年05月10日 19:19
  • 1459

k-d tree算法例子

k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构。主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。 应用背景   SIFT算法中做特征点匹配的时候就会...
  • luofeixiongsix
  • luofeixiongsix
  • 2016年02月06日 18:09
  • 1076

最近邻算法的实现:k-d tree

一、如何高效率地实现k近邻法?   在SIFT图像特征匹配等应用中,需要在高维特征空间中快速找到距离目标图像特征最近邻的那个特征点,往往需要进行比较的特征向量的数量很大,如果进行朴素最近邻搜索,也就...
  • ZHL30041839
  • ZHL30041839
  • 2013年07月09日 10:20
  • 9059

高级数据结构之K-D-TREE

k-d-tree(即k-dimensional tree)是一棵形如二叉树的一种非常重要的空间划分数据结构,尤其在多维数据访问中有重要应用。特别是机器学习中的kNN(k近邻)最常用到的搜索算法就是以k...
  • baimafujinji
  • baimafujinji
  • 2016年10月26日 21:42
  • 2850

K-D树 C++实现

K-D树主要是为了实现机器学习算法中的K近邻算法,单纯的K-D树只能实现最近邻,但是结合优先队列就可以实现K近邻了,这里只是把K-D树简单的实现了一下,经过简单测试,暂时没有发现重大bug。 #if...
  • codeforces_sphinx
  • codeforces_sphinx
  • 2013年04月12日 22:47
  • 3071

k-d Tree及其Java实现

本文内容基于An introductory tutoril on kd-trees 1.KDTree介绍 KDTree根据m维空间中的数据集D构建的二叉树,能加快常用于最近邻查找(在加快k-means...
  • riverflowrand
  • riverflowrand
  • 2016年07月15日 17:36
  • 926

kd树简介 在matlab下VLFeat中的kd-tree使用

先简要介绍knn——K近邻算法和kd-tree——kd树,然后介绍matlab环境中有关使用kd树的函数。k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构。主要应用于多...
  • damant
  • damant
  • 2016年03月17日 21:51
  • 4624

k近邻算法(KNN)及kd树简介(KD-Tree)

在使用k近邻法进行分类时,对新的实例,根据其k个最近邻的训练实例的类别,通过多数表决的方式进行预测。由于k近邻模型的特征空间一般是n维实数向量,所以距离的计算通常采用的是欧式距离。关键的是k值的选取,...
  • suibianshen2012
  • suibianshen2012
  • 2016年04月24日 17:40
  • 4777

k-d tree代码解析

 上一篇较详细地介绍了k-d树算法。本文来讲解具体的实现代码。   首先是一些数据结构的定义。我们先来定义单个数据,代码如下: //单个数据向量结构定义 struct _Examp...
  • qq_22928653
  • qq_22928653
  • 2014年11月07日 10:58
  • 185

k-d tree算法的研究

k-d tree算法的研究 2011-10-13 20:47:02 标签:k-d tree 物体识别 sift 休闲 最近邻搜索 原创作品,允许转载,转载时请务必以超链...
  • u010177286
  • u010177286
  • 2014年05月06日 22:25
  • 403
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:k-d tree 介绍
举报原因:
原因补充:

(最多只允许输入30个字)