二叉搜尋樹的查詢、最值查詢、插入和刪除
對於一棵二叉搜尋樹,如果不為空,它應該滿足以下三個特點:
1、樹上的任一結點,該結點的值都大於它的非空左子樹的值。
2、樹上的任一結點,該結點的值都小於它的非空右子樹的值。
3、任一結點的左右子樹都是二叉搜尋樹。
對於二叉搜尋樹的查詢,思路方法是:
1、從根結點開始查詢,如果樹為空,就返回NULL。
2、如果樹不空,就讓資料X和根結點的資料Data作比較。
3、如果X的值大於根結點的Data,就往右子樹中進行搜尋;如果X的值小於根結點的Data,就往左子樹中進行搜尋。
4、如果X的值等於Data,就表示查詢完成,返回該結點。
根據這個思路,我們很容易就能用遞迴的方式實現二叉搜尋樹的查詢:
第49行首先判斷樹空不空,空就返回
以上使用的是遞迴的方式實現,除了遞迴方式外,當然可以用迴圈的方式實現,而且迴圈的方式比遞迴執行效率更高。
迴圈查詢的方式這樣理解,同樣一開始判斷樹空不空,這裡用的是while判斷,只要樹不空就執行while裡面的語句,如果樹空,就直接執行第72行的return NULL。那麼當樹不空時,我們就開始做判斷,如果X大於Data,就讓
接下來到二叉搜尋樹的最值查詢。如何查詢最大值和最小值,根據二叉搜尋樹的特點,最大值一定是在樹的最右邊的結點上,而最小值就一定是在最左邊的結點上,那麼思路很清晰了,既可以用遞迴實現也可以迴圈實現。
遞迴實現方法:
對於遞迴查詢最大最小元素,都是一開始先判斷結點是否為空,如果為空就返回NULL,否則就一直往左(或者往右)遞迴,直到找到最左(或最右的結點為止),判斷最左或最右結點的方法就是遞迴到結點為
迴圈實現方法:
迴圈函式也是,一開始判斷結點空不空,不空就通過while迴圈,對於查詢最小值,只要BT—>Left不為NULL,就讓BT=BT—>Left,直到BT—>Left為空了,就證明找到了最左的葉結點,就退出了while迴圈,最後return出去。最大值方法一樣,迴圈右子樹直到找到最右結點,再return出去就可以了。
說完查詢後,接下來到插入。對於二叉搜尋樹的插入,插入結點後應該仍然要保持樹是二叉搜尋樹,也就是說插入的結點應該仍要保持該結點的左子樹比它小,右子樹比它大,可以採用的方法是改造一下遞迴查詢函式Find的方法。
程式第44到47行有兩個作用,第一個是當樹為空的時候,也就是往一棵空樹裡做插入操作,就申請一個結點空間,把X賦給BT—>Data,然後BT->Left=BT->Right=NULL,接著return這個結點。第二個作用就是用作樹不為空時插入結點,用一個例子來說明:
例如這樣一棵二叉搜尋樹,我們要把25插入到樹中,首先進入樹的根結點,25大於13,就往右子樹走,25小於35,就往左子樹走,接著25比22大了,再往右子樹走,這時發現結點22的右子樹為空了,也就是要做插入操作了。這時程式就會進入第49行做一次遞迴,第49行中的BT這時是22這個結點,此時22這個結點的Right是等於NULL的,所以這行裡的Insert函式裡就會執行第44行申請一個結點,把X賦給Data,BT->Right=BT->Left=NULL,然後return出來,return出來後就把這個新插入的結點賦給了BT->Right,也就是22這個結點的right這時就連線上新插入的結點了,程式的過程就是這樣。
最後到刪除操作,刪除操作分三種情況:
第一種情況,要刪除的結點是葉結點,也就是沒有左右子樹。這樣的情況只要把該結點的父結點指向NULL即可。
第二種情況,要刪除的結點有一個子樹(左子樹或右子樹),這時刪除該結點的方法就是讓該結點的父結點指向該結點的子樹即可。
第三種情況,要刪除的結點有左右兩個子樹,這個時候我們要採取的方法就是:從左子樹裡找一個最大值來代替,或者從右子樹裡找一個最小值來代替。拿這個例子來說明一下:
假如我們要刪除35這個結點,它有左右兩個子樹,這時我們可以選擇第一,從右子樹中找最小值,35的右子樹只有41,可以把41的值拷貝去替換掉35,然後刪除41這個節點。,這棵樹仍是二叉搜尋樹,滿足相應的關係。第二,從左子樹中找最大值,35的左子樹中最大值是28,就把28拷貝替換掉35的位置,然後刪除28這個結點,此時25順應就接到了22的右子樹的位置上,可以發現替換後樹仍然是一棵二叉搜尋樹。這樣做的好處是,把第三種情況要刪除的結點有兩個子樹變成要刪除的結點只有一個子樹。為什麼是這樣?因為我們可以想一下,左子樹中的最大值,肯定是在左子樹的最右邊,而這個結點肯定不會有兩個結點,要麼沒有要麼就只有一個結點。右子樹中的最小值同樣是這樣。
那麼程式要怎麼寫:
同樣一開始判斷樹空不空,如果空就輸出一條語句提示,接著判斷X和Data誰大誰小,因為先要找到那個要刪除的結點,所以X小的話就從左邊遞迴刪除,X大的話就從右邊遞迴刪除,當X等於Data了,也就是找到要刪除的那個結點了(第47行),然後就做判斷,判斷要刪除的這個結點有多少個子樹,如果有兩個子樹,這裡舉例用在右子樹中找最小值的方法,就呼叫FindMin函式找到右子樹中的最小值結點,把它賦給TP,然後把TP->Data也就是右子樹中找到的最小值拷貝給要刪除的點BT->Data,接著刪除掉原本要刪除的結點BT的右子樹中的最小值的結點。這樣就完成了替換了。那麼還有情況就是要刪除的結點只有一個子樹或者沒有子樹,前面方法說了就是把要刪除的結點的父結點指向該結點的左子樹或右子樹即可(第52-59行)。在左子樹中找最大值方法一樣,稍作修改即可。