1. 程式人生 > >kd樹原理及實現

kd樹原理及實現

優化 根節點 一半 索引 表示 建立索引 一次 所有 隨機

常用來作空間劃分及近鄰搜索,是二叉空間劃分樹的一個特例。通常,對於維度為k,數據點數為N的數據集,kd樹適用於N?2的k次方的情形。

1維數據的查詢

假設在數據庫的表格T中存儲了學生的語文成績chinese、數學成績math、英語成績english,如果要查詢語文成績介於30~93分的學生,如何處理?假設學生數量為N,如果順序查詢,則其時間復雜度為O(N),當學生規模很大時,其效率顯然很低,如果使用平衡二叉樹,則其時間復雜度為O(logN),能極大地提高查詢效率。平衡二叉樹示意圖為:

技術分享圖片

對於1維數據的查詢,使用平衡二叉樹建立索引即可。如果現在將查詢條件變為:語文成績介於30~93,數學成績結余30~90,又該如何處理呢?

如果分別使用平衡二叉樹對語文成績和數學成績建立索引,則需要先在語文成績中查詢得到集合S1,再在數學成績中查詢得到集合S2,然後計算S1和S2的交集,若|S1|=m,|S2|=n,則其時間復雜度為O(m*n),有沒有更好的辦法呢?

KD樹

針對多維數據索引,是否也存在類似的一維的索引方法呢?先看2維數據的集合示意圖:

技術分享圖片

如果先根據語文成績,將所有人的成績分成兩半,其中一半的語文成績<=c1,另一半的語文成績>c1,分別得到集合S1,S2;然後針對S1,根據數學成績分為兩半,其中一半的數學成績<=m1,另一半的數學成績>m1,分別得到S3,S4,針對S2,根據數學成績分為兩半,其中一半的數學成績<=m2

,另一半的數學成績>m2,分別得到S5,S6;再根據語文成績分別對S3,S4,S5,S6繼續執行類似劃分得到更小的集合,然後再在更小的集合上根據數學成績繼續,...

上面描述的就是構建KD樹的基本思路,其構建後的KD樹如下圖所示

技術分享圖片

如圖所示,l1左邊都是語文成績低於45分,l1右邊都是語文成績高於45分的;l2下方都是語文成績低於45分且數學成績低於50分的,l2上方都是語文成績低於45分且數學成績高於50分的,後面以此類推。下面的圖示,更清晰地表示了KD樹的結構及其對應的二叉樹:

技術分享圖片

樹的構建

a)切分維度選擇優化
構建開始前,對比數據點在各維度的分布情況,數據點在某一維度坐標值的方差越大分布越分散,方差越小分布越集中。從方差大的維度開始切分可以取得很好的切分效果及平衡性。
b)中值選擇優化
第一種,算法開始前,對原始數據點在所有維度進行一次排序,存儲下來,然後在後續的中值選擇中,無須每次都對其子集進行排序,提升了性能。
第二種,從原始數據點中隨機選擇固定數目的點,然後對其進行排序,每次從這些樣本點中取中值,來作為分割超平面。該方式在實踐中被證明可以取得很好性能及很好的平衡性。
本文采用常規的構建方式,以二維平面點(x,y)

的集合(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)為例結合下圖來說明k-d tree的構建過程。
a)構建根節點時,此時的切分維度為x,如上點集合在x維從小到大排序為(2,3),(4,7),(5,4),(7,2),(8,1),(9,6);其中值為(7,2)。(註:2,4,5,7,8,9在數學中的中值為(5 + 7)/2=6,但因該算法的中值需在點集合之內,所以本文中值計算用的是len(points)//2=3, points[3]=(7,2))
b)(2,3),(4,7),(5,4)掛在(7,2)節點的左子樹,(8,1),(9,6)掛在(7,2)節點的右子樹。
c)構建(7,2)節點的左子樹時,點集合(2,3),(4,7),(5,4)此時的切分維度為y,中值為(5,4)作為分割平面,(2,3)掛在其左子樹,(4,7)掛在其右子樹。
d)構建(7,2)節點的右子樹時,點集合(8,1),(9,6)此時的切分維度也為y,中值為(9,6)作為分割平面,(8,1)掛在其左子樹。至此k-d tree構建完成。

技術分享圖片

kd樹原理及實現