25 二叉排序樹

2021-08-16 02:12:51 字數 3189 閱讀 3210

之前所介紹的查詢方法都是對於已經排序號的陣列進行查詢,這樣的陣列雖然使用諸如二分查詢法這樣的方法可以較快的進行查詢,但是在陣列中插入或者刪除元素的時候,為了保持陣列中的相對順序保持不變,會使得插入和刪除變慢;對於沒有實現進行排序的陣列,插入的時候直接在最後一項進行插入即可,刪除某乙個元素是只需要首先將待刪除的元素與陣列中的最後乙個元素互換一下位置,在改變陣列長度(-1),即可實現刪除操作,但是對於這樣的陣列,查詢某乙個元素是很慢的,只能夠使用順序查詢法。

那有沒有一種方法,他的查詢速度不慢,並且插入和刪除的速度也還可以的呢?有的,這就是二叉排序樹。

二叉排序數(binary sort tree)又稱為二叉查詢樹,它或者是一棵空樹,或者是具有下列性質的二叉樹:

(1) 若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結構的值;

(2) 若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結構的值;

(3) 它的左、右子樹也分別為二叉排序樹(遞迴)。

具體如下圖所示

對於陣列的第乙個數字,將它作為二叉樹的根節點, 105 >70,所以 105 在根節點的右側;115 > 105 ,所以 115 在 105 的右側;104 > 70 且 104 < 105,所以 115 在 70 的右側,同時也在 105 的左側;67 < 70, 所以 67 在 70 的左側;46 < 70,且46 < 67, 所以 67 在 70 的左側,同時也在 67 的左側;99 > 70,99 < 105,99 < 104,所以 99 在 70 的右側,在 105 的左側,在 104 的左側;111 和 109 同理。

根據上面得到的二叉樹,我們知道通過中序遍歷的方式就可以得到排好順序的陣列,其中中序遍歷的順序先左子樹,再根節點,再右子樹,因此查詢操作的**如下

// 二叉樹的二叉鍊錶結點結構定義

typedef

struct bitnode

bitnode, *bitree;

// 遞迴查詢二叉排序樹 t 中是否存在 key

// 指標 f 指向 t 的雙親,其初始值呼叫值為 null

// 若查詢成功,則指標 p 指向該資料元素結點,並返回 true

// 否則指標 p 指向查詢路徑上訪問的最後乙個結點,並返回 false

status searchbst(bitree t, int key, bitree f, bitree *p)

else

if( key == t->data ) // 查詢成功

else

if( key < t->data )

else

}

在這裡有乙個變數是需要注意的,就是指標 *p 。在上面的查詢操作中,如果可以找到 key 那麼指標 *p 就指向查詢到的元素;如果沒有找到,指標 p 指向查詢路徑上訪問的最後乙個結點。也就是說指標始終指向最接近 key 的位置。

// 當二叉排序樹 t 中不存在關鍵字等於 key 的資料元素時,

// 插入 key 並返回 true,否則返回 false

status insertbst(bitree *t, int key)

else

if( key < p->data )

else

return true;

}else

}

加入我們需要在如下的一棵樹中刪除某一元素 a,我們可以分為三種情況進行討論。

(1) 如果刪除的結點是葉子結點,直接刪除就可以了;

(2) 如果刪除的結點只有左子樹或者只有右子樹,比如說 67 或者 115 ,對於這種結點,直接把它的子樹接到被刪除的結點即可;

(3) 如果刪除的結點既有左子樹又有右子樹,比如說105,這個時候是不能直接將左子樹或者右子樹接上去的,因為將 100 接上去的話,115 就沒有位置可以放了;同理將 115 接上去的話,100 又沒有位置可以放了。這個時候可以將該節點的前驅結點或者後繼節點放在被刪除的結點的位置,這樣就可以滿足要求了。

在上面的兩種情況中,前兩種情況可以看作是同一種情況。因為只有葉子結點可以看作只有乙個左子樹為空的結點,所以這兩情況在實現的過程中可以採用同樣的方法進行實現。

// 通過遞迴執行刪除

// 也可以使用之前編寫的查詢的方法,找到就刪除,找不到就不刪除

status deletebst(bitree *t, int key)

else

else

if( key < (*t)->data )

else

}}status delete(bitree *p)

else

if( (*p)->lchild == null ) //與上述同理

else

(*p)->data = s->data; // 將欲刪除點的直接前驅結點的資料帶入被刪除結點,只是帶入了資料

if( q != *p )

else

// s 沒有右子樹,不會執行 while 迴圈

free(s);

}return true;

}

在上述的**中,比較難懂的是if( q != *p )判斷的那個部分。這部分的**可以通過畫圖的方法進行解釋。

對於滿足if( q != *p )的情況,實際上是圖中去掉 105 結點的那種情況。如下圖所示,在這種情況下,p 是 105 的那個點,q 是 100 那個點,s 是 104 那個點,將 s 的值給 p 的值,之後將 s 左子樹放到 q 的右子樹

對於不滿足if( q != *p )的情況,即 s 點不存在右子樹,不會進入 while 迴圈更新 s ,實際上是圖中去掉 100 結點的那種情況。如下圖所示,在這種情況下,p 是 100 的那個點,q 是 100 那個點,s 是 99 那個點,將 s 的值給 p 的值,之後將 s 左子樹放到 q 的左子樹

二叉排序樹

在複習資料結構,把這個東西總結一下。這種結構是動態查詢表,這種動態是相對靜態查詢 順序查詢,折半查詢,分塊查詢等 來說的。對於各種靜態鍊錶,要達到查詢複雜度為o logn 必須要求有序 而要使插入刪除複雜度為o 1 必須是鍊錶儲存。動態查詢表就可以同時滿足這兩者。動態查詢表的特點是表結構本身在查詢過...

二叉排序樹

name 二叉排序樹相關操作 author unimen date 2011 10 8 13 14 21 刪除結點比較麻煩,總結如下 4大種情況 1 結點p無右孩子 將該點的左孩子變為其在雙親中的同位孩子 1 p為其雙親的左孩子時將其的左孩子變為雙親的左孩子 2 p為其雙親的右孩子時將其的左孩子變為...

二叉排序樹

include include include include struct tree node void insert node struct tree node int void pre order struct tree node void in order struct tree node ...