數據結構與算法-kd二叉樹(基礎)

前面探討的各類二叉樹,使用一個鍵值在樹中導航以執行必要的操做,二叉樹中每一個節點都有惟一的一個key值,經過key咱們能夠組織二叉查找樹、平衡樹、自適應樹、堆等,從某種意義上講,這是一維的二叉樹。假如咱們如今要研究二維平面上n個點的性質,怎麼將它們組織成二叉樹呢?若是是3維空間或者k維空間呢?對於一個節點來講,它不只僅只有一個key值,若是它處於k維空間,那麼會有k個key值。咱們必須探討出一種二叉樹,能夠組織k維空間上的節點。這就是kd二叉樹,全稱是k-dimension二叉樹,k維二叉樹。
本文以3維空間爲例來探討kd二叉樹的建立、查找、添加、刪除。
假設某3維空間上存在7個點,分別是(x1,y2,z3)、(x2,y3,z1)、(x3,y1,z2)、(x4,y4,z4)、(x5,y6,z7)、(x6,y7,z5)、(x7,y5,z6)。其中x(n)<x(n+1),y(n)<y(n+1),z(n)<z(n+1)
  • 建立
若是使用一維二叉查找樹來組織以上7個節點,咱們可使用x座標做爲鍵值(也可使用y或者z),判斷在哪裏插入這個點,從而存儲全部的點。爲了可以獨立地使用3個鍵值,咱們組建kd二叉樹時能夠交替使用x,y,z。在第一層,用x座標做爲識別符號,在第二層,使用y座標,在第三層,使用z座標,在第四層繼續使用x,以此類推,循環使用3個key值。最終的kd二叉查找樹多是下面這樣的:
其中,藍色的是x層,紅色的是y層,黑色的是z層。對於x層中的節點P來說,左子樹中任一節點的x的值都小於P節點的x值,右子樹中任一節點的x的值都大於P節點的x值。y層和z層同理。
以上的kd二叉查找樹是完美平衡的。相似一維二叉查找樹,相同的數據流,能夠構造出各類各樣的二叉查找樹。咱們但願構造出來的二叉查找樹儘量平衡,平衡意味着在查找數據時能夠跳過更多的節點,更加有效率的查找。一維二叉查找樹是怎麼建立的呢?能夠看數據結構與算法-二叉查找樹(DSW)。類比其中的建立邏輯,能夠探討出kd二叉樹的建立邏輯。
首先,咱們決定按照x->y->z的順序建立kd樹,根節點處於x層,那咱們將當前7個節點按照x的大小進行排序:
(x1,y2,z3)<(x2,y3,z1)<(x3,y1,z2)<(x4,y4,z4)<(x5,y6,z7)<(x6,y7,z5)<(x7,y5,z6)
取出中間節點(x4,y4,z4)做爲根節點,這樣作的好處是保證根節點的左右子樹節點個數相差不大於1。
第二層是根據節點的y值排序,左子樹節點排序以下:
(x3,y1,z2)<(x1,y2,z3)<(x2,y3,z1)
取出中間節點(x1,y2,z3)做爲左子樹的根節點。
以此類推,能夠將右子樹以及剩餘的節點安插到kd樹中,最終,kd樹是高度平衡的。
僞代碼以下:
def kd_tree(points, depth):
        if len(points) == 0:
                return None
        j = depth mod k
        將points數組中節點按照j維度大小排序
        獲取數組中間節點下標medium_index
        node = Node(points[medium_index])
        node.left = kd_tree(points[:medium_index], depth + 1)
        node.right = kd_tree(points[medium + 1:], depth +1)
        return node            複製代碼
僞代碼中使用遞歸來簡化邏輯,易於理解,實踐中不建議使用。
  • 查找
查找的邏輯比較簡單、直觀。若是查找(x7,y5,z6),逐層進行比較便可,核心邏輯在於不一樣層次比較的key值不一樣。動態圖以下:
  • 添加
添加的的邏輯也是至關直觀,就像查找同樣,這裏就很少說了。
  • 刪除
不管是一維的二叉查找樹仍是kd二叉查找樹,刪除邏輯都是比較複雜的。一維二叉查找樹刪除看這裏數據結構與算法-二叉查找樹。類比一維二叉查找樹的刪除,從最直觀的角度出發,咱們將kd樹刪除分紅3種狀況來考察。
一、刪除葉子節點
毫無疑問,刪除葉子節點直接釋放葉子節點空間便可,由於葉子節點沒有子樹須要處理,因此直接刪除。
二、刪除度爲1的節點
在一維二叉查找樹中,刪除度爲1的節點也是比較簡單的,直接將惟一的子樹提高到被刪除節點層次便可。可是在kd樹中,這樣處理是不行的。由於被刪除節點以及子樹處於不一樣的層次,提高子樹到被刪除節點的層次是不可行的。假如如今有被刪除節點P,子樹根節點爲Q,Q存在子節點R,其中,P層比較x值,Q層比較y值,若是節點Q是節點P的左節點,那麼x(Q)<x(P),節點R是節點Q的左節點,那麼y(R)<y(Q)。假設刪除P以後,提高Q節點到P原有的位置,須要保證x(R)<x(Q),可是咱們只能保證y(R)<y(Q),因此直接提高子樹的層次是不可行的。
三、刪除度爲2的節點
類比一維二叉查找樹中度爲2節點的刪除邏輯,無非是合併刪除或者複製刪除,其中合併刪除明顯是不可行的,緣由也是子樹合併以後依然要提高子樹到被刪除節點的層次,這是不可行的。複製刪除能夠嗎?假設被刪除節點爲P,左子樹爲Q,右子樹爲R。複製刪除的核心邏輯是在Q中找到最大的節點替換P或者在R中找到最小的節點替換P。本質是,可以替換P的節點要大於Q中任意節點,小於R中任意節點。將其擴展到kd二叉查找樹,什麼樣的節點能夠替換被刪除節點呢?
假設被刪除節點爲P,咱們將以P節點爲根的子樹截取出來,問題就轉化爲如何刪除kd二叉查找樹的根節點。當前的難點在於找到一個什麼樣的節點來替換根節點,這就須要觀察根節點的特性了。還記得插入節點的邏輯嗎?假設插入新節點Q,首先將Q節點的x維度的值和根節點x維度的值比較,大於則轉向右子樹,小於則轉向左子樹,在此過程當中,其餘維度的值不起做用。也就是說,根節點的x維度的值大於左子樹中任意節點的x維度的值,小於右子樹中任意節點的x維度的值,其餘維度的值大小沒有要求。那麼答案就顯而易見了,可以替換根節點的只有兩個,首先是左子樹中x維度值最大的節點,其次是右子樹中x維度值最小的節點。怎麼找呢?沒什麼好的辦法,由於這兩個可替換節點的位置沒有固定的規律,只能遍歷全部節點來尋找。就算找到了,若是該節點也是度爲2的節點,那麼刪除它的邏輯和上面是同樣的,依然要遍歷尋找可替換的節點,直到找到葉子節點,才能夠直接刪除。
刪除度爲1的節點和刪除度爲2的節點邏輯是同樣的,找到可替換節點才行。
僞代碼以下:
假設q節點是被刪除節點右子樹的根節點,下面是查找q子樹中i維度值最小節點的邏輯
smallest(q, i) {
        min = q;
        if q->left != 0
                lt = smallest(q->left, i);
                if min->el.keys[i] >= lt->el.keys[i]
                        min = lt;
        if q->right != 0
                rt = smallest(q->right, i);
                if min->el.keys[i] >= rt->el.keys[i]
                        min = rt;
        return min;
}複製代碼
僞代碼的邏輯是使用遞歸,逐個比較每一個節點的x維度值的大小。顯然,這種方式不夠優秀,咱們還能夠繼續改進。改進點在於,若是咱們知道某節點R是比較x維度的值,那咱們就不用遍歷R節點的右子樹了,由於R節點的左子樹任意節點的x維度的值小於R節點x維度的值,而R節點x維度的值是小於R節點右子樹任意節點x維度值的。因此,若是咱們檢測到當前比較節點比較的是x維度的值,直接選擇它的左子樹便可。
僞代碼以下:
假設q節點是被刪除節點右子樹的根節點,下面是查找q子樹中i維度值最小節點的邏輯
smallest(q, i, j) {
        min = q;
        if i == j
                if q->left != 0
                        min = q = q->left;
                        j = j + 1
                else 
                        return q;
        if q->left != 0
                lt = smallest(q->left, i, (j + 1) mode k);
                if min->el.keys[i] >= lt->el.keys[i]
                        min = lt;
        if q->right != 0
                rt = smallest(q->right, i, (j + 1) mode k);
                if min->el.keys[i] >= rt->el.keys[i]
                        min = rt;
        return min;
}複製代碼
到此爲止,咱們已經介紹了kd二叉查找樹的建立、查找、添加、刪除算法。可是咱們依然有不少困惑沒有解決,好比說添加或者刪除會破壞kd樹的平衡,怎麼處理?刪除算法複雜且效率不高,怎麼改進?kd二叉查找樹有哪些應用?等等,這些問題在下一篇文章進行探討。

數據結構與算法-kd二叉樹(kNN)
node

相關文章
相關標籤/搜索