小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

基于c語言實(shí)現(xiàn)的二叉查找樹

 心不留意外塵 2016-04-22

 http://blog.csdn.net/chenbolyfxinsui/article/details/25461817                                                                                                                                                                                                                         2014/5/10 by lyf  @七里外風(fēng)車磨坊 

         今天我們來討論討論二叉查找樹,首先我們來看看二叉查找樹的概念.

       

        什么是二叉查找樹呢?顧名思義,二叉查找樹是按照二叉樹的結(jié)構(gòu)來進(jìn)行組織的,這樣的一棵二叉樹可以采用鏈表的形式來加以描述,這樣的鏈表包含兩個(gè)指針域       (left_child,right_child),以及一個(gè)數(shù)據(jù)域(data)

樹中的每個(gè)節(jié)點(diǎn)NODE,都采用鏈表中的節(jié)點(diǎn)加以表示,left_child與right_child分別指向樹中節(jié)點(diǎn)的左孩子與右孩子,若樹中的節(jié)點(diǎn)沒有左孩子或者右孩子,則對(duì)應(yīng)的left_child或者right_child為空NULL。樹的葉子節(jié)點(diǎn)左右孩子均為空。二叉查找樹除了滿足上述基本的性質(zhì)之外,還應(yīng)滿足如下的二叉查找樹的性質(zhì):

設(shè)a為二叉查找樹中的一個(gè)節(jié)點(diǎn)(這里假設(shè)使用data(a)為取節(jié)點(diǎn)a對(duì)應(yīng)的數(shù)據(jù)域,后面的操作于此相同)

           1.  若b為a的左子樹中的一個(gè)節(jié)點(diǎn),則必定有data(a)>data(b),即以a為根節(jié)點(diǎn)的左子樹中的任意節(jié)點(diǎn)b的數(shù)據(jù)域都不大于a節(jié)點(diǎn)的數(shù)據(jù)域。

            2.  若b為a的右子樹中的一個(gè)節(jié)點(diǎn),則必定有data(a)<=data(b),即以a為根節(jié)點(diǎn)的右子樹中的任意節(jié)點(diǎn)b的數(shù)據(jù)與都不小于a節(jié)點(diǎn)的數(shù)據(jù)域。

            3.   二叉查找樹,按照中序遍歷該二叉查找樹,其輸出結(jié)果是有序的。

 

如下圖所示即為滿足二叉查找樹的一棵樹


下面我們采用c語言來描述二叉查找樹以及對(duì)二叉查找樹的相關(guān)操作。


一、使用c語言定義二叉樹的數(shù)據(jù)結(jié)構(gòu)

 typedef  struct  TreeNode{

     void *data;   //數(shù)據(jù)域

    struct  TreeNode* left_child; //左指針域

    struct  TreeNode* right_child;//右指針域

}TreeNode,*BinaryTree;

根據(jù)二叉查找樹的定義,我們采用結(jié)構(gòu)體定義二叉查找樹,該結(jié)構(gòu)體中包含了一個(gè)數(shù)據(jù)域(data),和兩個(gè)指向該結(jié)構(gòu)體類型的兩個(gè)指針域(left_child,right_child).分別用于指向當(dāng)前節(jié)點(diǎn)的左孩子和右孩子。其實(shí)在這個(gè)結(jié)構(gòu)體中我們還可以增加一個(gè)指針域,用于指向節(jié)點(diǎn)的父節(jié)點(diǎn)(parent)。


二.二叉查找樹的基本操作.


在二叉查找樹中,我們提供了創(chuàng)建一棵二叉查找樹,插入新的節(jié)點(diǎn)到二叉查找樹中,以及遍歷這顆二叉查找樹,刪除二叉查找樹中的某個(gè)節(jié)點(diǎn),查找節(jié)點(diǎn)是否存在二叉查找樹中.


1.首先我么來討論插入一個(gè)節(jié)點(diǎn)到二叉查找樹中。

分析:既然要插入一個(gè)節(jié)點(diǎn)到二叉查找樹中,我們首先是要找到這個(gè)節(jié)點(diǎn)應(yīng)該插入到二叉查找樹的什么位置上。

顯而易見的是新插入的節(jié)點(diǎn)一定是作為一個(gè)葉子節(jié)點(diǎn)存在二叉查找樹中的。

      繼續(xù),怎樣找到這個(gè)節(jié)點(diǎn)應(yīng)該插入到二叉查找樹的什么位置上呢? 

       其實(shí)我們只需要用待插入的節(jié)點(diǎn)從二叉查找樹的根節(jié)點(diǎn)遍歷這顆二叉查找樹,用待插入的節(jié)點(diǎn)去和二叉查找樹中的節(jié)點(diǎn)進(jìn)行比較,若待插入的節(jié)點(diǎn)比當(dāng)前節(jié)點(diǎn)的值小

       則進(jìn)入到當(dāng)前節(jié)點(diǎn)的左子樹中進(jìn)行比較,否則進(jìn)入到當(dāng)前節(jié)點(diǎn)的右子樹中進(jìn)行比較,如此返回操作.直到當(dāng)前的節(jié)點(diǎn)的左或右孩子為空,根據(jù)當(dāng)前節(jié)點(diǎn)的大小判斷

       待插入的節(jié)點(diǎn)應(yīng)該插入在當(dāng)前節(jié)點(diǎn)的左孩子(若當(dāng)前節(jié)點(diǎn)值大于待插入節(jié)點(diǎn)的值),還是右孩子中。

注意:需要注意的是若二叉查找樹最開始就沒有,即為一棵空二叉查找樹,則我們就直接用待插入的新節(jié)點(diǎn)作為整個(gè)二叉查找樹的根節(jié)點(diǎn)即可.


如下圖模擬11插入到一棵二叉查找樹中


                                                                   

     


                                                         



code:使用c語言實(shí)現(xiàn)二叉樹的插入操作。//插入節(jié)點(diǎn)到二叉查找樹中

//插入節(jié)點(diǎn)到二叉查找樹中

void InsertNode(BinaryTree* root,const void *data,int size,int(*comp)(const void*,const void*)){

      

       if(*root==NULL){//二叉樹為一棵空樹

              InitBinaryTree(root,data,size);  //將當(dāng)前節(jié)點(diǎn)作為二叉查找樹的根節(jié)點(diǎn)

              return;

       }

    

       //分配待插入的新節(jié)點(diǎn)

       TreeNode*newNode=(TreeNode*)malloc(sizeof(TreeNode));

       newNode->data=(void*)malloc(size);

       memcpy(newNode->data,data,size);

       newNode->left_child=newNode->right_child=NULL;

      

       //對(duì)節(jié)點(diǎn)進(jìn)行遍歷操作,尋找查找位置

       TreeNode*node=(*root);

       while(node->left_child!=newNode&&node->right_child!=newNode){

              if(comp(data,node->data)<0){//待插入的節(jié)點(diǎn)值大于該節(jié)點(diǎn)的值

                     if(node->left_child==NULL)

                           node->left_child=newNode;

                     else

                           node=node->left_child;

              }else{

                     if(node->right_child==NULL){

                           node->right_child=newNode;

                     }

                     else

                           node=node->right_child;

              }

       }

}

//創(chuàng)建一棵二叉查找樹,即初始化根節(jié)點(diǎn)

void InitBinaryTree(BinaryTree* root,const void *data,int size){

       *root=(BinaryTree)malloc(sizeof(TreeNode));

       (*root)->data=(void*)malloc(size);

       (*root)->left_child=(*root)->right_child=NULL;

        memcpy((*root)->data,data,size);

}


2.二叉查找樹的遍歷操作。

  根據(jù)二叉查找樹的性質(zhì)可以知道,按照中序的方式遍歷一棵二叉查找樹,遍歷出來的順序即為一個(gè)有序的序列.

  遍歷操作的時(shí)候,我們按照遞歸的方式進(jìn)行遍歷,即先遍歷二叉查找樹的左子樹,在遍歷二叉查找樹的右子樹。

  當(dāng)然我們也可以借助于棧進(jìn)行非遞歸遍歷操作.


code:c語言遍歷二叉查找樹(遞歸方式)

//遍歷這顆二叉排序樹

void showTree(BinaryTree root,void (*print)(const void*)){

   if(root!=NULL){

          showTree(root->left_child,print);

      print(root->data);

          showTree(root->right_child,print);

   }

}

3.查找值是否存在于二叉查找樹中.

查找值是否存在于二叉查找樹中只需要,只需要遍歷該二叉查找樹,使用二叉查找樹中的每個(gè)節(jié)點(diǎn)和帶查找的值進(jìn)行比較,若該節(jié)點(diǎn)的值小于待查找節(jié)點(diǎn)的值

則進(jìn)入到節(jié)點(diǎn)的左孩子中查找操作,否則進(jìn)入到右孩子節(jié)點(diǎn)中查找操作.直到找到節(jié)點(diǎn)的值為待查找的值,否則二叉查找樹中不存在待查找的節(jié)點(diǎn).

cod:c 語言實(shí)現(xiàn)查找節(jié)點(diǎn)是否存在于二叉查找樹中

//查找指定的節(jié)點(diǎn)是否存在于二叉排序樹中

int findDataInBinarryTree(BinaryTreeroot,void *data,int(*comp)(const void*,const void*)){

       TreeNode*node=root;

       while(NULL!=node){

              if(comp(node->data,data)==0){

                     return 1;

              }else if(comp(node->data,data)>0){

                     node=node->left_child;

              }else{

                     node=node->right_child;

              }

       }

       return 0;

}

遞歸方式實(shí)現(xiàn)查找:

int  findDataInBinarryTree(BinaryTree  root,void *data,int (*comp)(const void*,const void*)){

 

       if(NULL!=root){

              if(comp(root->data,data)==0){

                     return 1;

              }

              if(comp(root->data,data)>0){

                     return  findDataInBinarryTree(root->left_child,data,comp);

              }else{

                     return  findDataInBinarryTree(root->right_child,data,comp);

              }

       }

       return 0;

}



4.二叉查找樹的刪除操作

二叉查找樹的刪除,應(yīng)該算是這幾個(gè)操作中比較復(fù)雜的一個(gè)。

二叉查找樹在刪除節(jié)點(diǎn)后應(yīng)該保證二叉查找樹的性質(zhì),因此在執(zhí)行刪除操作后任然需要保證這一棵二叉樹為一棵二叉查找樹

刪除操作進(jìn)行的第一步操作是首先要查找到待刪除的節(jié)點(diǎn)。

(1)首先我們想到的是待刪除的節(jié)點(diǎn)為葉子節(jié)點(diǎn),即我們只需要將待刪除節(jié)點(diǎn)(葉子節(jié)點(diǎn))直接從二叉查找樹中刪除即可,因?yàn)閯h除葉子節(jié)點(diǎn)不會(huì)破壞二叉查找樹的性質(zhì)。

   如下圖所示:(需要注意的是若待刪除的節(jié)點(diǎn)為根節(jié)點(diǎn),即只有一個(gè)節(jié)點(diǎn)的樹,直接將根節(jié)點(diǎn)賦值為空,刪除根節(jié)點(diǎn)即可)


                                            

(2) 若待刪除的節(jié)點(diǎn)不是葉子節(jié)點(diǎn),但是以待刪除節(jié)點(diǎn)為根節(jié)點(diǎn)的子樹是一棵單子樹(即待刪除節(jié)點(diǎn)的左右孩子只有一個(gè)存在),則需要做的操作是將待刪除節(jié)點(diǎn)父節(jié)點(diǎn)的指向修改為待刪除節(jié)點(diǎn)的孩子節(jié)點(diǎn)(即左孩子或右孩子),然后刪除該節(jié)點(diǎn)即可(特別的當(dāng)刪除的節(jié)點(diǎn)為根節(jié)點(diǎn),則將待刪除節(jié)點(diǎn)的左孩子或右孩子的節(jié)點(diǎn)作為根節(jié)點(diǎn)即可)。

              

(3)若待刪除的節(jié)點(diǎn)的左右子樹均不為空,則我們?cè)谶M(jìn)行節(jié)點(diǎn)刪除操作后需要保持二叉查找樹的性質(zhì),則需要做的操作是以待刪除節(jié)點(diǎn)的左孩子為根節(jié)點(diǎn)的子樹k中尋找最大的節(jié)點(diǎn)值r(遍歷子樹的右子樹),將節(jié)點(diǎn)r的值賦值給待刪除節(jié)點(diǎn)h的值,將節(jié)點(diǎn)r的左子樹賦值給節(jié)點(diǎn)r父節(jié)點(diǎn)的右孩子,刪除節(jié)點(diǎn)r即可.(如圖七所示)



                        

             特別地,當(dāng)子樹k的右孩子為空,則節(jié)點(diǎn)k即為待刪除節(jié)點(diǎn)h的左子樹中的最大節(jié)點(diǎn),則將節(jié)點(diǎn)k的值賦給待刪除節(jié)點(diǎn)h,讓節(jié)點(diǎn)k的父節(jié)點(diǎn)的左孩子指向節(jié)點(diǎn)k的左孩子,刪除節(jié)點(diǎn)k即可。

                       

實(shí)際上在子樹k的右孩子為空的時(shí)候我們還要另外的一種思考方式,即改變待刪除節(jié)點(diǎn)h的父節(jié)點(diǎn)c指向,若待刪除節(jié)點(diǎn)h,為父節(jié)點(diǎn)c的左孩子,則父節(jié)點(diǎn)c的左孩子指向?yàn)榇齽h除節(jié)點(diǎn)h的左孩子節(jié)點(diǎn)k,若待刪除節(jié)點(diǎn)h為右孩子,則父節(jié)點(diǎn)c的右孩子指向?yàn)榇齽h除節(jié)點(diǎn)h的左孩子節(jié)點(diǎn)k,將待刪除的節(jié)點(diǎn)h的右孩子m賦值給,待刪除節(jié)點(diǎn)h的左孩子k的右孩子,然后刪除節(jié)點(diǎn)h即可.(此種方式不需向圖八的方式給待刪除節(jié)點(diǎn)賦值操作).


               

 


code:c語言實(shí)現(xiàn)刪除二叉查找樹中的某個(gè)節(jié)點(diǎn)

//刪除二叉查找樹中的某個(gè)節(jié)點(diǎn)

void delTreeNode(BinaryTree* root,void *data,int size,int(*comp)(const void*,const void*)){

 //在刪除一棵二叉查找樹的時(shí)候,需要保持二叉排序樹的特性,需要分幾種情況進(jìn)行刪除操作

 //1.若待刪除的節(jié)點(diǎn)為葉子節(jié)點(diǎn)直接將該節(jié)點(diǎn)刪除即可,并將父節(jié)點(diǎn)的對(duì)應(yīng)的指針賦值為空

   TreeNode*delnode=(*root); //待刪除節(jié)點(diǎn)

   TreeNode*delnodeParent=(*root);//待刪除節(jié)點(diǎn)的父節(jié)點(diǎn)

   while(delnode!=NULL){

          if(comp(data,delnode->data)<0){

                delnodeParent=delnode;

                 delnode=delnode->left_child;

          }else if(comp(data,delnode->data)>0){

                 delnodeParent=delnode;

                 delnode=delnode->right_child;

          }else{

                 //查找到待刪除的節(jié)點(diǎn)

                 break;

          }

   }

 

   if(NULL==delnode)

   {

          printf("沒有查找到待刪除的節(jié)點(diǎn)!");

          return;

   }

    //待刪除的節(jié)點(diǎn)為葉子節(jié)點(diǎn)

   if(delnode->left_child==NULL&&delnode->right_child==NULL){

          //若待刪除的節(jié)點(diǎn)為根節(jié)點(diǎn),則直接刪除該節(jié)點(diǎn)即可,樹為空樹

          if(delnode==*root)

                 (*root)=NULL;

          else if(delnodeParent->left_child==delnode)//若待刪除的節(jié)點(diǎn)為父節(jié)點(diǎn)的左孩子

                 delnodeParent->left_child=NULL;

          else

                 delnodeParent->right_child=NULL;

          //釋放該節(jié)點(diǎn)的內(nèi)存空間

          free(delnode);

          delnode=NULL;

   }

 

   //以待刪除節(jié)點(diǎn)為根節(jié)點(diǎn)的子樹為單子樹,則修改待刪除節(jié)點(diǎn)父節(jié)點(diǎn)的孩子節(jié)點(diǎn)為待刪除節(jié)點(diǎn)的孩子

   else if(delnode->left_child==NULL||delnode->right_child==NULL){

         //同樣若待刪除的節(jié)點(diǎn)為根節(jié)點(diǎn),該變根節(jié)點(diǎn)的指針指向?yàn)榇齽h除節(jié)點(diǎn)的孩子節(jié)點(diǎn)

          if(delnode==*root){

                 if(delnode->left_child!=NULL)

                        (*root)=delnode->left_child;

                 else

                        (*root)=delnode->right_child;

          }     

          //待刪除節(jié)點(diǎn)不為根節(jié)點(diǎn)

          else{

                 if(delnodeParent->left_child==delnode){

                        if(delnode->left_child!=NULL)

                             delnodeParent->left_child=delnode->left_child;

                        else

                             delnodeParent->left_child=delnode->right_child;

                 }else{

                         if(delnode->left_child!=NULL)

                              delnodeParent->right_child=delnode->left_child;

                        else

                             delnodeParent->right_child=delnode->right_child;

                 }

          }

          free(delnode);

          delnode=NULL;

   }

 

   //以待刪除節(jié)點(diǎn)的子樹都不為空,這里的做法是以待刪除節(jié)點(diǎn)的左孩子為根節(jié)點(diǎn)的子樹中尋找最大的節(jié)點(diǎn),

   //即遍歷到該子樹最右邊的孩子節(jié)點(diǎn)將該節(jié)點(diǎn)的值賦值給待刪除節(jié)點(diǎn),刪除該節(jié)點(diǎn)即可

   else if(delnode->left_child!=NULL&&delnode->right_child!=NULL){

          TreeNode* tempNode=delnode->left_child;

          TreeNode*tempParentNode=delnode->left_child;

          while(tempNode->right_child!=NULL){

                 tempParentNode=tempNode;

                 tempNode=tempNode->right_child;

          }

         memcpy(delnode->data,tempNode->data,size);

          if(tempNode==tempParentNode){

                 //待刪除節(jié)點(diǎn)的左孩子節(jié)點(diǎn)的右子樹為空,則待刪除節(jié)點(diǎn)的左孩子即為尋找的節(jié)點(diǎn),將尋找到的節(jié)點(diǎn)的左孩子連接到待刪除節(jié)點(diǎn)的左子樹上

                delnode->left_child=tempNode->left_child;

          }else{

                 //找到待刪除節(jié)點(diǎn)的左孩子節(jié)點(diǎn)的右子樹中較大節(jié)點(diǎn),將較大節(jié)點(diǎn)的父節(jié)點(diǎn)的右子樹連接到較大節(jié)點(diǎn)的左子樹

                tempParentNode->right_child=tempNode->left_child;

          }

          free(tempNode);

          tempNode=NULL;

   }

}


5.釋放二叉查找樹的內(nèi)存空間

二叉查找樹的空間是使用指針進(jìn)行內(nèi)存空間動(dòng)態(tài)分配的,所以在使用完畢之后我們不要忘記釋放所分配的內(nèi)存空間.

釋放一棵二叉查找樹的空間可以這樣操作,釋放這棵樹的左孩子,釋放這顆樹的右孩子,釋放根節(jié)點(diǎn).


code:c語言實(shí)現(xiàn)釋放二叉查找樹的動(dòng)態(tài)內(nèi)存空間

//釋放二叉查找樹的空間

void freeBinarySpace(BinaryTree* root){

       //釋放二叉樹的內(nèi)存空間

       if(*root!=NULL){

               //釋放左子樹

              freeBinarySpace(&(*root)->left_child);

              //釋放右子樹

              freeBinarySpace(&(*root)->right_child);

              //釋放本身的內(nèi)存空間

              free((*root)->data);//釋放數(shù)據(jù)域

              (*root)->data=NULL;

              free((*root));

              *root=NULL;

       }

}

 

6.其他的一些輔助公共操作方法

int intBig(const  void*data_one,const void*data_two){return *(int*)data_one-*(int*)data_two;}

//打印輸出函數(shù)

void   printInt(const void*data){printf("%d",*(int*)data);}


三.二叉查找樹的算法分析

在二叉查找樹中查找一個(gè)關(guān)鍵字,恰好是走了一條從二叉樹的根節(jié)點(diǎn)到該節(jié)點(diǎn)的路徑的過程.在二叉查找樹中查找一個(gè)關(guān)鍵字與二叉樹中節(jié)點(diǎn)的比較次數(shù)最大不超過該二叉樹的深度.

當(dāng)含有n個(gè)節(jié)點(diǎn)的二叉查找樹的平均查找長(zhǎng)度是取決于二叉查找樹的形態(tài)的,當(dāng)在進(jìn)行二叉查找樹插入n個(gè)數(shù)據(jù)為有序的n個(gè)數(shù)據(jù)的時(shí)候,則得到的二叉查找樹為一棵單子樹,則樹的深度為n 則平均查找長(zhǎng)度為(n+1)/2.這是最極端的情況

 最好的情況就是,建立的n個(gè)數(shù)據(jù)的二叉排序樹的形態(tài)與二分查找的判定樹相當(dāng),其平均查找長(zhǎng)度與log2(n) 成正比。

二叉查找樹的各項(xiàng)基本操作的運(yùn)行時(shí)間都是o(h),h為二叉排序樹的高度,含有n個(gè)節(jié)點(diǎn)的二叉查找樹在最優(yōu)的情況下樹的高度為o(logn),所以要使得二叉查找樹最優(yōu),就的使得

二叉查找樹的高度為o(logn),可以通過平衡二叉樹對(duì)其進(jìn)行優(yōu)化。


畢!


參考:算法導(dǎo)論第二版,數(shù)據(jù)結(jié)構(gòu)(c語言版)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多