基於樹的查找--------------二叉查找樹

1,爲什麼要分析二叉查找樹(又名:二叉排序樹)?
           正真分析開源代碼或軟件開發中,基於樹的查找是難免會有的,現在多用的是
紅黑樹,而紅黑樹是基於平衡樹的,而平衡樹是基於查找樹的,而查找樹是基於二叉
樹的,二叉樹大家都很熟悉,所以先從查找樹開始分析。這樣才便於分析平衡樹,最終
的分析對象將會是紅黑樹。
-------------------------------------------------------------------------------------------
2,什麼是二叉查找樹?
      遞歸定義如下:
           (1),若左子樹不爲空,則左子數上所有結點的值均小於根結點的值。
           (2),若右子樹不爲空,則右子數上所有結點的值均大於根結點的值。
           (3),它的左右子樹也分別是二叉查找樹。

下面就是一個二叉查找樹,任何一個子樹,都是右孩子最大,根節點其次,左孩子最小。


-------------------------------------------------------------------------------------------
3,二叉查找樹的建立。
      二叉查找樹的創建和插入是聯繫在一起的,樹的創建就是使用的插入方式。從一顆空樹開始
不停地插入,直到稱爲一顆完整的樹。
例如:
  1. 39 int create_bst(bstree**bst)
  2.  40 {
  3.  41 int a[]={24,12,53,28,45,90};
  4.  42 int length= ARRAY_SIZE(a);
  5.  43 int i = 0;
  6.  44
  7.  45 *bst = NULL;
  8.  46
  9.  47 for (i=0;i<length;i++){
  10.  48 insert_bstree(bst,a[i]);
  11.  49 }
  12.  50
  13.  51 return 0;
  14.  52 }
現在要創建一個6個節點的數,插入的順序是24,12,53,28,45,90.
創建樹的圖示如下:

--------------------------------------------------------------------------------------------
4,二叉查找樹的插入。
實例代碼:
  1. 20 void insert_bstree(bstree**root,int key)
  2.  21 {
  3.  22 if ((*root)==NULL) {
  4.  23 (*root)=(bstree *)malloc(sizeof(bstree));
  5.  24 if ((*root)==NULL)
  6.  25 printf("file %s,line %d:malloc error\n",__FILE__,__LINE__);
  7.  26 (*root)->key= key;
  8.  27 (*root)->lchild=NULL;
  9.  28 (*root)->rchild=NULL;
  10.  29 } else {
  11.  30 if (key ==(*root)->key){
  12.  31 ;
  13.  32 } else if (key<(*root)->key){
  14.  33 insert_bstree(&((*root)->lchild),key);
  15.  34 } else
  16.  35 insert_bstree(&((*root)->rchild),key);
  17.  36 }
  18.  37 }
該插入的地方只可能是葉子節點,所以這兒使用了一個遞歸,先要找到插入的位置,
如果key值小,則往左走,key值大則往右走,直到爲空,然後才找到位置,創建節
點將節點插入其中。
-------------------------------------------------------------------------------------------------------------
5,二叉查找樹的刪除。
二叉查找樹的刪除比較麻煩,因爲刪除一個節點後還要保證其仍然是一個二叉查找樹。
現在根據要刪除節點的類型將情況分爲四類。
情況1:刪除的節點爲葉子節點。

例如,上面的樹上有三個葉子,分別是12,45,90,無論刪除那個節點仍然是二叉查找樹,
故只需將該節點釋放,然後將其父節點的指針域做一些“善後”工作即可。實現代碼如下:

  1. case 1:
  2.        free(pnode);//釋放節點。
  3.        if (father->lchild== pnode)//對其父節點做出指針域的善後工作。
  4.             father->lchild=NULL;
  5.         else
  6.             father->rchild=NULL;
  7.         goto out2;//退出
情況2:刪除的節點有右子樹沒有左子樹。

該情況也不算複雜,首先要考慮要刪除的節點是否可能是根節點,如果是根節點,也好辦,
因爲這是一個只有“右半邊”的查找樹,直接把根給刪了,根節點的右子樹作爲根節點。
如果要刪除的節點不是根節點,也好辦,如圖中的28,直接將右子樹交給自己的父親
“照看”。實現代碼如下:

  1. case 2:
  2.     if (father ==NULL){//如果要刪除的節點爲根節點情況。
  3.          *root= pnode->rchild;//將要刪除節點的右子樹作爲新的根。
  4.           free(pnode);//釋放要刪除的節點。
  5.           goto out1;//退出。
  6.     }
  7.     if (father->lchild== pnode) //不是根節點的情況,
  8.          father->lchild= pnode->rchild;//如果刪除的節點是父節點的左孩子,現在就吧自己的右子樹作爲
  9.     else                                //父節點的左孩子。
  10.           father->rchild= pnode->rchild;//如果刪除的節點是父節點的右孩子,現在就把自己的右子樹作爲
  11.     free(pnode);                          //父節點的右孩子。
  12.     goto out2;//釋放節點後退出
情況3:刪除的節點有左子樹沒有右子樹。
          該情況和情況2類似,故不作詳細的分析了。
           實現代碼如下:

  1. case 3:
  2.     if (father ==NULL){
  3.         *root= pnode->lchild;
  4.          free(pnode);
  5.          goto out1;
  6.      }
  7.      if (father->lchild== pnode)
  8.           father->lchild= pnode->lchild;
  9.      else
  10.            father->rchild= pnode->lchild;
  11.       free(pnode);
  12.       goto out2;
情況4:刪除的節點有左子樹有右子樹。

該情況是這四種情況中最複雜的一種了,如何刪除該節點後仍然保證其是二叉查找樹呢。
例如上圖的24和53.

算法思想如下:
<1>,找到要刪除節點pnode的中序序列的直接前驅s。(53的就是45,24的就是12)
<2>,將pnode的左子樹交給pnode的父親“照顧”。
<3>,將pnode的右子樹交給pnode的直接前驅S"照顧"。
經過以上的三步,就可以保證Pnode的左右子樹各有“所養”。

算法實現代碼如下:

  1. 132 case 4:
  2. 133 s = pnode->lchild;  //球pnode中序序列的直接前驅。
  3. 134 while (s->rchild)
  4. 135 s = s->rchild;        
  5. 136 if (father ==NULL){  //如果刪除的節點是根節點
  6. 137 *root = pnode->lchild; //左子樹成爲新的根。
  7. 138 s->rchild= pnode->rchild;//右子樹交給直接前驅S
  8. 139 free(pnode);
  9. 140 goto out2;
  10. 141 }
  11. 142 if (father->rchild== pnode){
  12. 143 father->rchild= pnode->lchild;//左子樹交給父節點。
  13. 144 s->rchild= pnode->rchild;//右子樹交給直接前驅S
  14. 145 free(pnode);
  15. 146 goto out2;
  16. 147 }else{
  17. 148 father->lchild= pnode->rchild;//左子樹交給父節點。
  18. 149 s->lchild= pnode->lchild;//右子樹交給直接前驅S
  19. 150 free(pnode);
  20. 151 goto out2;
  21. 152
---------------------------------------------------------------------------------------------
6,實例代碼。(完整代碼)
 bstree.rar 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章