1. 概念:
Binary-search tree(BST)是一顆二叉樹,每個樹上的節點都有<=1個父親節點,ROOT節點沒有父親節點。同時每個樹上的節點都有[0,2]個孩子節點(left child AND right child)。每個節點都包含有各自的KEY值以及相應的satellite data。其中KEY是幾種BST基本操作的主要操作對象。
2. BST的特別性質:
BST任何一顆子樹上的三個節點left, parent, right. 滿足條件left.key<parent.key<=right.key,一顆典型的BST如下圖所示:

觀察之后不難發現如果對BST進行PREORDER walk(先序遍歷),得到:2, 5, 5, 6, 7, 8 剛好是升序排列。
P.S 所謂PREORDER walk,就是要訪問以ROOT為根的樹,先要訪問ROOT.left, 然后訪問ROOT, 最后訪問ROOT.right。
用pseudocode的形式的話,就是這樣:
PREORDER-WALK(x) 1 if(x!=NIL) 2 PREORDER-WALK(x.left) 3 print x.key 4 PREORDER-WALK(x.right)
3. BST的幾種基本操作:
SEARCH, MINIMUM, MAXIMUM, PREDECESSOR, SUCCESSOR, INSERT, and DELETE.
2.1 SEARCH:
TREE-SEARCH(x, k) INPUT: BST ROOT node, value k. OUPUT: node in BST whose key equals to k 1 if(x==NIL) OR (k==x.key) 2 return x 3 if(k<x.key) 4 return TREE-SEARCH(x.left, k) 5 else return TREE-SEARCH(x.right, k)
算法解釋:當我們要找到key值為k的節點在BST中的位置,我們一般調用上述函數TREE-SEARCH(ROOT, k)(ROOT為BST的根節點)。如果ROOT節點的key值等於k,則我們要找的節點就是根節點ROOT。如果不是,根據BST的特殊性質:
left.key<parent.key<=right.key
我們比較當前節點的key值,如果current.key<k,則要在當前節點的左子樹中查找,反之,則在右子樹中查找。[line3-line5]
2.2 MINIMUM:
TREE-MINIMUM(x) INPUT: BST ROOT node OUTPUT: the smallest key in BST 1 if(x==NIL) return; 2 while(x.left!=NIL) 3 x=x.left 4 return x.key
算法解釋:一個BST的最左葉子節點的key值就是BST所有key值中最小的。這個根據BST的特殊性質很容易推導出來:
因為 left.key < parent.key < parent.key < .... < root.key
有了MINIMUM算法,MAXIMUM算法也就有了:
2.3 MAXIMUM:
TREE-MAXIMUM(x) INPUT: BST ROOT node OUTPUT: the smallest key in BST 1 if(x==NIL) return; 2 while(x.right!=NIL) 3 x=x.right 4 return x.key
2.4 SUCCESSOR:
TREE-SUCCESSOR(x) INPUT: arbitray node x in BST OUTPUT: x's successor node 1 if(x.right!=NIL) 2 TREE-MINIMUM(x.right) 3 y=x.parent 4 while(y!=NIL AND y.left==x) 5 x=y 6 y=y.parent 7 return y
這里說明一下SUCCESSOR的含義,x的SUCCESSOR滿足x.key<=x.SUCCESSOR.key,並且x.SUCCESSOR.key是距離x.key最近的值,即x.SUCCESSOR.key是x.key的最小上限(minimum ceiling)
算法思想:因為在BST中,有x.key<=x.right.key。所以如果某BST的節點x有右孩子節點(x.right!=NIL),則利用TREE-MINIMUM在x的右子樹中查找即可。如果x不存在右孩子,則檢查x是否有父親節點並且x必須是父親節點的左孩子,只有這樣才有parent.key>parent.left.key。但找到x的parent就結束了嗎,還沒有,就像TREE-MINIMUM函數一樣,我們需要逐層查找,當找到符合條件的parent后,我們遞歸的繼續檢查parent節點有沒有parent.parent節點符不符合上述條件。知道訪問到的parent節點不符合該條件為止。
2.5 INSERT:
TREE-INSERT(T, z) 1 y=NIL 2 x=T.ROOT 3 while(x!=NIL) 4 y=x 5 if(z.key<x.key) 6 x=x.left 7 else x=x.right 8 z.p=y 9 if(y==NIL) T.ROOT=z 10 if(z.key<y.key) y.left=z 11 else y.right=z
用一張圖來解釋該算法:

虛線指向的key值為13的節點是即將要插入BST的點,其他的點已經在BST上。可以看到從ROOT節點出發,根據“左小右大”規則,我們很快找到了key值為15的節點,因為13<15,所以檢查‘15’節點是否有左孩子,結果是沒有,所以把13放到15的左孩子節點處。我們可以想象如果要插入的節點key是16會怎么樣?同樣的,我們還是找到‘15’節點,但是16>15,所以繼續往‘右’查找,找到‘17’,因為16<17,看17有沒有左孩子節點,沒有,所以會把‘16’放到‘17’的左孩子節點處。
2.6 TRANSPLANT:
TRANPLANT(T, u, v) 1 if (u.parent==NIL) T.ROOT=v 2 else if(u==u.parent;.left) u.parent.left=v 3 else u.parent.right=v 4 if(v!=NIL) v.parent=u.parent
還是用一張圖來展示比較清晰:

2.7 DELETE:
TREE-DELETE(T, z) 1 if z.left==NIL 2 TRANSPLANT(T, z, z.right) 3 else if z.right == NIL 4 TRANSPLANT(T, z, z.left) 5 else y=TREE-MINIMUM(z.right) 6 if y.parent!=z 7 TRANSPLANT(T, y, y.right) 8 y.right=z.right 9 y.right.parent=y 10 TRANSPLANT(T, z, y) 11 y.left=z.left 12 y.left.parent=y
DELETE算法相對來說比較麻煩一些,需要考慮的情況要多一些。因為要在刪除BST的某一節點后仍能維持BST的基本性質。假設要刪除的節點是z,z的key值為k。根據“左小右大”原則,z.left.key<k<=z.right.key.如果這時候吧z刪了,誰來接替z的位置呢並且保證解體之后的新BST滿足性質呢?方法我們可以根據性質來推導:我們要找到一個除了z以外的節點在BST中,該節點假設為P,那么P.key要大於z.left.key並且P.key>=z.right.key,實際上很明顯的,唯一符合要求的就是z的SUCCESSOR。
如果z只有左子樹或者只有右子樹,那么問題就很簡單,使z的左孩子或者右孩子來頂替z的位置即可。如果z的左子樹和右子樹都存在,那么問題稍微復雜一點,我們要在BST上找到z的SUCCESSOR。如此一來,這個問題又變成我們之前討論的SUCCESSOR問題了,總共有兩種情形:
第一種情形:z的右孩子就是我們要找的z的SUCCESSOR,那我們就直接TRANSPLANT移植以z.right為根的子樹到z的位置中去。
第二種情形:z的右孩子不是我們要找的z的SUCCESSOR,那我們就從z.right出發,‘深入’尋找知道找到z的SUCCESSOR,假設為Q。
那么我們讓Q成為z.right的父親節點,並且讓以Q為根的右子樹(因為是根據“最左原則”找到的Q,Q要么是以z.right為根的left-most葉子節點,要么Q只有右子樹。)取代Q的位置。最后讓Q取代z的位置。。。很繞。。。還是見圖吧。

