Kd-樹 其實是K-dimension tree的縮寫,是對數據點在k維空間中划分的一種數據結構。其實,Kd-樹是一種平衡二叉樹。
舉一示例:
假設有六個二維數據點 = {(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},數據點位於二維空間中。為了能有效的找到最近鄰,Kd-樹采用分而治之的思想,即將整個空間划分為幾個小部分。六個二維數據點生成的Kd-樹的圖為:
對於擁有n個已知點的kD-Tree,其復雜度如下:
- 構建:O(log2n)
- 插入:O(log n)
- 刪除:O(log n)
- 查詢:O(n1-1/k+m) m---每次要搜索的最近點個數
一 Kd-樹的構建
Kd-樹是一個二叉樹,每個節點表示的是一個空間范圍。下表表示的是Kd-樹中每個節點中主要包含的數據結構。Range域表示的是節點包含的空間范圍。Node-data域就是數據集中的某一個n維數據點。分割超面是通過數據點Node-Data並垂直於軸split的平面,分割超面將整個空間分割成兩個子空間。令split域的值為i,如果空間Range中某個數據點的第i維數據小於Node-Data[i],那么,它就屬於該節點空間的左子空間,否則就屬於右子空間。Left,Right域分別表示由左子空間和右子空間空的數據點構成的Kd-樹。
域名 |
數據類型 |
描述 |
Node-Data |
數據矢量 |
數據集中某個數據點,是n維矢量 |
Range |
空間矢量 |
該節點所代表的空間范圍 |
Split |
整數 |
垂直於分割超面的方向軸序號 |
Left |
Kd-tree |
由位於該節點分割超面左子空間內所有數據點構成的Kd-樹 |
Right |
Kd-tree |
由位於該節點分割超面左子空間內所有數據點構成的Kd-樹 |
Parent |
Kd-tree |
父節點 |
構建Kd-樹的偽碼為:
算法:構建Kd-tree
輸入:數據點集Data_Set,和其所在的空間。
輸出:Kd,類型為Kd-tree
1 if data-set is null ,return 空的Kd-tree
2 調用節點生成程序
(1)確定split域:對於所有描述子數據(特征矢量),統計他們在每個維度上的數據方差,挑選出方差中最大值,對應的維就是split域的值。數據方差大說明沿該坐標軸方向上數據點分散的比較開。這個方向上,進行數據分割可以獲得最好的分辨率。
(2)確定Node-Data域,數據點集Data-Set按照第split維的值排序,位於正中間的那個數據點 被選為Node-Data,Data-Set` =Data-Set\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,LeftRange)建立的Kd-tree
設置:left的parent域(父節點)為Kd
:right =由(dataright,RightRange)建立的Kd-tree
設置:right的parent域為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 為左子空間,包含節點(2,3),(5,4),(4,7),另一部分為右子空間。包含節點(9,6),(8,1)
這個構建過程是一個遞歸過程。重復上述過程,直至只包含一個節點。