C#通過KD樹進行距離最近點的查找
本文首先介紹Kd-Tree的構(gòu)造方法,然后介紹Kd-Tree的搜索流程及代碼實現(xiàn),最后給出本人利用C#語言實現(xiàn)的二維KD樹代碼。這也是我自己動手實現(xiàn)的第一個樹形的數(shù)據(jù)結(jié)構(gòu)。理解上難免會有偏差,敬請各位多多斧正。
1. KD樹介紹
Kd-Tree(KD樹),即K-dimensional tree,是一種高維索引樹形數(shù)據(jù)結(jié)構(gòu),常用于在大規(guī)模的高維數(shù)據(jù)空間進行最鄰近查找和近似最鄰近查找。我實現(xiàn)的KD樹是二維的Kd - tree。目的是在點集中尋找最近點。參考資料是Kd-Tree的百度百科。并且根據(jù)百度百科的邏輯組織了代碼。
2. KD樹的數(shù)學(xué)解釋
3. KD樹的構(gòu)造方法
這里是用的二維點集進行構(gòu)造Kd-tree。三維的與此類似。
樹中每個節(jié)點的數(shù)據(jù)類型:
public class KDTreeNode { /// <summary> /// 分裂點 /// </summary> public Point DivisionPoint { get; set; } /// <summary> /// 分裂類型 /// </summary> public EnumDivisionType DivisionType { get; set; } /// <summary> /// 左子節(jié)點 /// </summary> public KDTreeNode LeftChild { get; set; } /// <summary> /// 右子節(jié)點 /// </summary> public KDTreeNode RightChild { get; set; } }
3.1 KD樹構(gòu)造邏輯流程
- 將所有的點放入集合a中
- 對集合所有點的X坐標求得方差xv,Y坐標求得方差yv
- 如果xv > yv,則對集合a根據(jù)X坐標進行排序。如果 yv > xv,則對集合a根據(jù)y坐標進行排序。
- 得到排序后a集合的中位數(shù)m。則以m為斷點,將[0,m-2]索引的點放到a1集合中。將[m,a.count]索引的點放到a2的集合中(m點的索引為m-1)。
- 構(gòu)建節(jié)點,節(jié)點的值為a[m-1],如果操作集合中節(jié)點的個數(shù)大于1,則左節(jié)點對[0,m-2]重復(fù)2-5步,右節(jié)點為對[m,a.count]重復(fù)2-5步;反之,則該節(jié)點為葉子節(jié)點。
3.2 代碼實現(xiàn)
private KDTreeNode CreateTreeNode(List<Point> pointList) { if (pointList.Count > 0) { // 計算方差 double xObtainVariance = ObtainVariance(CreateXList(pointList)); double yObtainVariance = ObtainVariance(CreateYList(pointList)); // 根據(jù)方差確定分裂維度 EnumDivisionType divisionType = SortListByXOrYVariances(xObtainVariance, yObtainVariance, ref pointList); // 獲得中位數(shù) Point medianPoint = ObtainMedian(pointList); int medianIndex = pointList.Count / 2; // 構(gòu)建節(jié)點 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的總體搜索流程先根據(jù)普通的查找找到一個最近的葉子節(jié)點。但是這個葉子節(jié)點不一定是最近的點。再進行回溯的操作找到最近點。
4.1 KD樹搜索邏輯流程
- 對于根據(jù)點集構(gòu)建的樹t,以及查找點p.將根節(jié)點作為節(jié)點t進行如下的操作
- 如果t為葉子節(jié)點。則得到最近點n的值為t的分裂點的值,跳到第5步;如果t不是葉子節(jié)點,進行第3步
- 則確定t的分裂方式,如果是按照x軸進行分裂,則用p的x值與節(jié)點的分裂點的x值進行比較,反之則進行Y坐標的比較
- 如果p的比較值小于t的比較值,則將t指定為t的左孩子節(jié)點。反之將t指定為t的右孩子節(jié)點,執(zhí)行第2步
- 定義檢索點m,將m設(shè)置為n
- 計算m與p的距離d1,n與m的距離d2。
- 如果d1 >= d2且有父節(jié)點,則將m的父節(jié)點作為m的值執(zhí)行5步,若沒有父節(jié)點,則得到真正的最近點TN; 如果d1 < d2就表示n點不是最近點,執(zhí)行第8步
- 若n有兄弟節(jié)點,則 n = n的兄弟節(jié)點;若n沒有兄弟節(jié)點,則 n = n的父節(jié)點。刪除原來的n節(jié)點。將m的值設(shè)置為新的n節(jié)點;執(zhí)行第6步。
4.2 代碼實現(xiàn)
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) { // 利用堆棧記錄查詢的路徑,由于樹節(jié)點中沒有記載父節(jié)點的原因 backtrackStack.Push(node); } if (node.DivisionType == EnumDivisionType.X) { return DFSXsearch(node,searchPoint); } else { return DFSYsearch(node, searchPoint); } } private Point BacktrcakSearch(Point searchPoint,Point nearestPoint) { // 如果記錄路徑的堆棧為空則表示已經(jīng)回溯到根節(jié)點,則查到的最近點就是真正的最近點 if (backtrackStack.IsEmpty()) { return nearestPoint; } else { KDTreeNode trackNode = backtrackStack.Pop(); // 分別求回溯點與最近點距查找點的距離 double backtrackDistance = ObtainDistanFromTwoPoint(searchPoint, trackNode.DivisionPoint); double nearestPointDistance = ObtainDistanFromTwoPoint(searchPoint, nearestPoint); if (backtrackDistance < nearestPointDistance) { // 深拷貝節(jié)點的目的是為了避免損壞樹 KDTreeNode searchNode = new KDTreeNode() { DivisionPoint = trackNode.DivisionPoint, DivisionType = trackNode.DivisionType, LeftChild = trackNode.LeftChild, RightChild = trackNode.RightChild }; nearestPoint = DFSBackTrackingSearch(searchNode, searchPoint); } // 遞歸到根節(jié)點 return BacktrcakSearch(searchPoint, nearestPoint); } }
5. 源碼交流
https://github.com/CreamMilk/C-Kd-Tree
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:C#中可枚舉類型詳解
欄 目:C#教程
下一篇:C#使用yield關(guān)鍵字構(gòu)建迭代器詳解
本文標題:C#通過KD樹進行距離最近點的查找
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/5453.html
您可能感興趣的文章
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并打開的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的方法
- 01-10C#通過Semaphore類控制線程隊列的方法
- 01-10C#通過正則表達式實現(xiàn)提取網(wǎng)頁中的圖片
- 01-10使用設(shè)計模式中的工廠方法模式進行C#編程的示例講解
- 01-10C#中AS和IS關(guān)鍵字的用法
- 01-10C#操作LINQ to SQL組件進行數(shù)據(jù)庫建模的基本教程
- 01-10C#使用Protocol Buffer(ProtoBuf)進行Unity中的Socket通信
- 01-10C#在Unity游戲開發(fā)中進行多線程編程的方法
- 01-10淺談Visual C#進行圖像處理(讀取、保存以及對像素的訪問)


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻 器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機閱讀
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結(jié)
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 01-11ajax實現(xiàn)頁面的局部加載
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢dedecms什么時候用欄目交叉功能?