正真分析開源代碼或軟件開發中,基於樹的查找是難免會有的,現在多用的是
紅黑樹,而紅黑樹是基於平衡樹的,而平衡樹是基於查找樹的,而查找樹是基於二叉
樹的,二叉樹大家都很熟悉,所以先從查找樹開始分析。這樣才便於分析平衡樹,最終
的分析對象將會是紅黑樹。
-------------------------------------------------------------------------------------------
2,什麼是二叉查找樹?
遞歸定義如下:
(1),若左子樹不爲空,則左子數上所有結點的值均小於根結點的值。
(2),若右子樹不爲空,則右子數上所有結點的值均大於根結點的值。
(3),它的左右子樹也分別是二叉查找樹。
下面就是一個二叉查找樹,任何一個子樹,都是右孩子最大,根節點其次,左孩子最小。
-------------------------------------------------------------------------------------------
3,二叉查找樹的建立。
二叉查找樹的創建和插入是聯繫在一起的,樹的創建就是使用的插入方式。從一顆空樹開始
不停地插入,直到稱爲一顆完整的樹。
例如:
- 39 int create_bst(bstree**bst)
- 40 {
- 41 int a[]={24,12,53,28,45,90};
- 42 int length= ARRAY_SIZE(a);
- 43 int i
= 0;
- 44
- 45 *bst
= NULL;
- 46
- 47 for
(i=0;i<length;i++){
- 48 insert_bstree(bst,a[i]);
- 49 }
- 50
- 51 return 0;
- 52 }
創建樹的圖示如下:
--------------------------------------------------------------------------------------------
4,二叉查找樹的插入。
實例代碼:
- 20 void insert_bstree(bstree**root,int
key)
- 21 {
- 22 if
((*root)==NULL)
{
- 23 (*root)=(bstree
*)malloc(sizeof(bstree));
- 24 if
((*root)==NULL)
- 25 printf("file %s,line %d:malloc error\n",__FILE__,__LINE__);
- 26 (*root)->key=
key;
- 27 (*root)->lchild=NULL;
- 28 (*root)->rchild=NULL;
- 29 }
else {
- 30 if
(key ==(*root)->key){
- 31 ;
- 32 }
else if (key<(*root)->key){
- 33 insert_bstree(&((*root)->lchild),key);
- 34 }
else
- 35 insert_bstree(&((*root)->rchild),key);
- 36 }
- 37 }
如果key值小,則往左走,key值大則往右走,直到爲空,然後才找到位置,創建節
點將節點插入其中。
-------------------------------------------------------------------------------------------------------------
5,二叉查找樹的刪除。
二叉查找樹的刪除比較麻煩,因爲刪除一個節點後還要保證其仍然是一個二叉查找樹。
現在根據要刪除節點的類型將情況分爲四類。
情況1:刪除的節點爲葉子節點。
例如,上面的樹上有三個葉子,分別是12,45,90,無論刪除那個節點仍然是二叉查找樹,
故只需將該節點釋放,然後將其父節點的指針域做一些“善後”工作即可。實現代碼如下:
- case 1:
- free(pnode);//釋放節點。
- if
(father->lchild== pnode)//對其父節點做出指針域的善後工作。
- father->lchild=NULL;
- else
- father->rchild=NULL;
- goto out2;//退出
該情況也不算複雜,首先要考慮要刪除的節點是否可能是根節點,如果是根節點,也好辦,
因爲這是一個只有“右半邊”的查找樹,直接把根給刪了,根節點的右子樹作爲根節點。
如果要刪除的節點不是根節點,也好辦,如圖中的28,直接將右子樹交給自己的父親
“照看”。實現代碼如下:
- case 2:
- if
(father ==NULL){//如果要刪除的節點爲根節點情況。
- *root= pnode->rchild;//將要刪除節點的右子樹作爲新的根。
- free(pnode);//釋放要刪除的節點。
- goto out1;//退出。
- }
- if
(father->lchild== pnode) //不是根節點的情況,
- father->lchild= pnode->rchild;//如果刪除的節點是父節點的左孩子,現在就吧自己的右子樹作爲
- else //父節點的左孩子。
- father->rchild= pnode->rchild;//如果刪除的節點是父節點的右孩子,現在就把自己的右子樹作爲
- free(pnode); //父節點的右孩子。
- goto out2;//釋放節點後退出
該情況和情況2類似,故不作詳細的分析了。
實現代碼如下:
- case 3:
- if
(father ==NULL){
- *root= pnode->lchild;
- free(pnode);
- goto out1;
- }
- if
(father->lchild== pnode)
- father->lchild= pnode->lchild;
- else
- father->rchild= pnode->lchild;
- free(pnode);
- goto out2;
該情況是這四種情況中最複雜的一種了,如何刪除該節點後仍然保證其是二叉查找樹呢。
例如上圖的24和53.
算法思想如下:
<1>,找到要刪除節點pnode的中序序列的直接前驅s。(53的就是45,24的就是12)
<2>,將pnode的左子樹交給pnode的父親“照顧”。
<3>,將pnode的右子樹交給pnode的直接前驅S"照顧"。
經過以上的三步,就可以保證Pnode的左右子樹各有“所養”。
算法實現代碼如下:
- 132 case 4:
- 133 s = pnode->lchild; //球pnode中序序列的直接前驅。
- 134 while
(s->rchild)
- 135 s = s->rchild;
- 136 if
(father ==NULL){ //如果刪除的節點是根節點
- 137 *root
= pnode->lchild; //左子樹成爲新的根。
- 138 s->rchild= pnode->rchild;//右子樹交給直接前驅S
- 139 free(pnode);
- 140 goto out2;
- 141 }
- 142 if
(father->rchild== pnode){
- 143 father->rchild= pnode->lchild;//左子樹交給父節點。
- 144 s->rchild= pnode->rchild;//右子樹交給直接前驅S
- 145 free(pnode);
- 146 goto out2;
- 147 }else{
- 148 father->lchild= pnode->rchild;//左子樹交給父節點。
- 149 s->lchild= pnode->lchild;//右子樹交給直接前驅S
- 150 free(pnode);
- 151 goto out2;
- 152
6,實例代碼。(完整代碼)
bstree.rar