博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 通过KD树进行距离最近点的查找.
阅读量:6657 次
发布时间:2019-06-25

本文共 4010 字,大约阅读时间需要 13 分钟。

本文首先介绍Kd-Tree的构造方法,然后介绍Kd-Tree的搜索流程及代码实现,最后给出本人利用C#语言实现的二维KD树代码。这也是我自己动手实现的第一个树形的数据结构。理解上难免会有偏差,敬请各位多多斧正。

1. KD树介绍

Kd-Tree(KD树),即K-dimensional tree,是一种高维索引树形数据结构,常用于在大规模的高维数据空间进行最邻近查找和近似最邻近查找。我实现的KD树是二维的Kd - tree。目的是在点集中寻找最近点。参考资料是Kd-Tree的百度百科。并且根据百度百科的逻辑组织了代码。

2. KD树的数学解释

3. KD树的构造方法

这里是用的二维点集进行构造Kd-tree。三维的与此类似。

树中每个节点的数据类型:

public class KDTreeNode    {        ///         /// 分裂点        ///         public Point DivisionPoint { get; set; }        ///         /// 分裂类型        ///         public EnumDivisionType DivisionType { get; set; }        ///         /// 左子节点        ///         public KDTreeNode LeftChild { get; set; }        ///         /// 右子节点        ///         public KDTreeNode RightChild { get; set; }    }

3.1 KD树构造逻辑流程

  1. 将所有的点放入集合a中
  2. 对集合所有点的X坐标求得方差xv,Y坐标求得方差yv
  3. 如果xv > yv,则对集合a根据X坐标进行排序。如果 yv > xv,则对集合a根据y坐标进行排序。
  4. 得到排序后a集合的中位数m。则以m为断点,将[0,m-2]索引的点放到a1集合中。将[m,a.count]索引的点放到a2的集合中(m点的索引为m-1)。
  5. 构建节点,节点的值为a[m-1],如果操作集合中节点的个数大于1,则左节点对[0,m-2]重复2-5步,右节点为对[m,a.count]重复2-5步;反之,则该节点为叶子节点。

3.2 代码实现

private KDTreeNode CreateTreeNode(List
pointList){ if (pointList.Count > 0) { // 计算方差 double xObtainVariance = ObtainVariance(CreateXList(pointList)); double yObtainVariance = ObtainVariance(CreateYList(pointList)); // 根据方差确定分裂维度 EnumDivisionType divisionType = SortListByXOrYVariances(xObtainVariance, yObtainVariance, ref pointList); // 获得中位数 Point medianPoint = ObtainMedian(pointList); int medianIndex = pointList.Count / 2; // 构建节点 KDTreeNode treeNode = new KDTreeNode() { DivisionPoint = medianPoint, DivisionType = divisionType, LeftChild = CreateTreeNode(pointList.Take(medianIndex).ToList()), RightChild = CreateTreeNode(pointList.Skip(medianIndex + 1).ToList()) }; return treeNode; } else { return null; }}

4. KD树搜索方法

Kd-Tree的总体搜索流程先根据普通的查找找到一个最近的叶子节点。但是这个叶子节点不一定是最近的点。再进行回溯的操作找到最近点。

4.1 KD树搜索逻辑流程

  1. 对于根据点集构建的树t,以及查找点p.将根节点作为节点t进行如下的操作
  2. 如果t为叶子节点。则得到最近点n的值为t的分裂点的值,跳到第5步;如果t不是叶子节点,进行第3步
  3. 则确定t的分裂方式,如果是按照x轴进行分裂,则用p的x值与节点的分裂点的x值进行比较,反之则进行Y坐标的比较
  4. 如果p的比较值小于t的比较值,则将t指定为t的左孩子节点。反之将t指定为t的右孩子节点,执行第2步
  5. 定义检索点m,将m设置为n
  6. 计算m与p的距离d1,n与m的距离d2。
  7. 如果d1 >= d2且有父节点,则将m的父节点作为m的值执行5步,若没有父节点,则得到真正的最近点TN; 如果d1 < d2就表示n点不是最近点,执行第8步
  8. 若n有兄弟节点,则 n = n的兄弟节点;若n没有兄弟节点,则 n = n的父节点。删除原来的n节点。将m的值设置为新的n节点;执行第6步。

4.2 代码实现

public Point FindNearest(Point searchPoint){    // 按照查找方式寻找最近点    Point nearestPoint = DFSSearch(this.rootNode, searchPoint);        // 进行回溯    return BacktrcakSearch(searchPoint, nearestPoint);}private Point DFSSearch(KDTreeNode node,Point searchPoint,bool pushStack = true){    if(pushStack == true)    {        // 利用堆栈记录查询的路径,由于树节点中没有记载父节点的原因        backtrackStack.Push(node);    }    if (node.DivisionType == EnumDivisionType.X)    {       return DFSXsearch(node,searchPoint);    }    else    {       return DFSYsearch(node, searchPoint);    }}private Point BacktrcakSearch(Point searchPoint,Point nearestPoint){    // 如果记录路径的堆栈为空则表示已经回溯到根节点,则查到的最近点就是真正的最近点    if (backtrackStack.IsEmpty())    {        return nearestPoint;    }    else    {        KDTreeNode trackNode = backtrackStack.Pop();                // 分别求回溯点与最近点距查找点的距离        double backtrackDistance = ObtainDistanFromTwoPoint(searchPoint,         trackNode.DivisionPoint);        double nearestPointDistance = ObtainDistanFromTwoPoint(searchPoint, nearestPoint);               if (backtrackDistance < nearestPointDistance)       {           // 深拷贝节点的目的是为了避免损坏树           KDTreeNode searchNode = new KDTreeNode()           {                DivisionPoint = trackNode.DivisionPoint,                DivisionType = trackNode.DivisionType,                LeftChild = trackNode.LeftChild,                RightChild = trackNode.RightChild            };           nearestPoint = DFSBackTrackingSearch(searchNode, searchPoint);      }      // 递归到根节点      return BacktrcakSearch(searchPoint, nearestPoint);   }}

5. 源码交流

转载于:https://www.cnblogs.com/MaFeng0213/p/7598886.html

你可能感兴趣的文章
按日期、时间批量删除文件
查看>>
Tomcat使用IDEA远程Debug调试
查看>>
tmpFile.renameTo(classFile) failed解决
查看>>
php中括号定义数组
查看>>
linux centos 搭建svn
查看>>
2.Nginx优化
查看>>
如何获取浏览器URL中查询字符串的参数?
查看>>
Array.prototype.slice.call()为什么能将类数组转换为数组
查看>>
matlab练习程序(粒子群优化PSO)
查看>>
POJ1157 LITTLE SHOP OF FLOWERS
查看>>
防盗链与springboot代理模式(图片文件转发)
查看>>
【2019-07-01】正确的观念
查看>>
套接字文件描述符消耗小细节分析
查看>>
这不是任何人的退役文
查看>>
mysql 拼接
查看>>
hdu1372(BFS)
查看>>
javascript定义对象写法-------作者:虫虫3000 日期:2009-9-7 10:14:9
查看>>
质检总局-版权局
查看>>
Delphi TstringList Stringlist的特殊用法
查看>>
python中快捷键f5_python shell(用的IDIE)程序编辑器中、菜单Edit→怎么没有Run Script选项、快捷键Ctrl+F5也没有、?...
查看>>