二叉樹的基本操作

以下爲二叉樹的性質

//樹的存儲形式-->雙親,孩子,孩子雙親,孩子兄弟表示法 
//二叉樹的存儲
//完全二叉樹:如果一棵有N個節點的二叉樹的結構與滿二叉樹的前N個
//節點的結構相同,稱爲完全二叉樹
//二叉樹的性質:若規定根層數爲1則第i層最多有2的i-1次方
//              最多有2的i次方減一個節點
//對任何二叉樹,如果其葉節點個數爲n0,度爲2的非葉節點爲n2則
//              n0=n2+1
// 推導過程 N = n0+n1+n2 (可得邊數爲N-1)N-1 = n1+2*n2 
//深度爲log2(n+1)(向上取整) 
//可推出 n0=n2+1
// 完全二叉樹如果爲奇數節點沒有隻有左孩子的節點,否則爲1
//無論如何都不會有隻有右孩子的節點,與完全二叉樹的定義不符合
//如1000個節點的完全二叉樹
//高度:10 葉子爲:500 非葉子:500 只有左孩子:1 只有右孩子:0 
//順序結構:根節點序號i
//左孩子2i+1若越界則沒有左孩子
//右孩子2i+2若越界則沒有右孩子
//雙親節點爲(i-1)/2若爲負數則本身無雙親節點 
//鏈式結構:孩子表示法+雙親孩子表示法//二叉樹表示法:孩子表示法
//

binary tree -->BTree(二叉樹)
#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
typedef struct BTNode
{
    struct BTNode* _pLeft;
    struct BTNode* _pRight;
    char _data;
 } BTNode;
 
 //////////////////////////////////////////////////
//二叉樹概念:(1)空樹 (2)根節點+左子樹+右子樹 
// 
 
 
 
 
 
 //BTree.c

//建立二叉樹節點
BTNode * BuyBTNode(char data)
{
     BTNode *pNewNode = (BTNode*)malloc(sizeof(BTNode));
     //強制將 malloc(sizeof(BTNode))分配的void*類型的指針
     //轉化爲BTNode*型 
     if(NULL == pNewNode)
     {
         //assert(0);//返回一個錯誤信息 
         return NULL;
     }
     pNewNode->_data = data;
     pNewNode->_pRight = NULL;
     pNewNode->_pLeft = NULL;
     
     return pNewNode;
    } 
 //二叉樹的遍歷(操作):
 //規則:1.每個節點只能遍歷一次
 //      2.約定遍歷方式:前中後序 根左右 左根右 左右根
 //       前序:根節點-->根節點的左子樹+根節點的右子樹
 //       中序:根節點左子樹-->根節點-->根節點的右子樹
 //       後序: 根節點左子樹根-->節點的右子樹-->根節點

//以下爲二叉樹的前中後序遍歷
     void PreOrder(BTNode *pRoot)
     {
         if(pRoot)
         {
         printf("%c ",pRoot->_data);
        PreOrder(pRoot->_pLeft);
        PreOrder(pRoot->_pRight);
        }    
     }
     void InOrder(BTNode *pRoot)
     {
         if(pRoot)
         {
        InOrder(pRoot->_pLeft);
         printf("%c ",pRoot->_data);
        InOrder(pRoot->_pRight);
        }    
     }
    void PostOrder(BTNode *pRoot)
     {
         if(pRoot)
         {
        PostOrder(pRoot->_pLeft);
        PostOrder(pRoot->_pRight);
        printf("%c ",pRoot->_data);
        }    
     }
//二叉樹的創建
//"ABDCEF" 
 void CreatBinTree(BTNode **pRoot,char *str,int size,int *index,char invalid)
 {//char invalid 參數字符爲作爲空節點的標誌字符 
     assert(pRoot);
     assert(index);
     if(*index<size&&str[*index]!=invalid)//不能用節點的NULL判斷 
     {
     
         *pRoot = BuyBTNode(str[*index]);//創建樹的根 
     
         ++(*index);
     //創建根節點的左子樹
        CreatBinTree(&((*pRoot)->_pLeft), str, size,index,invalid);
        //注意在函數裏++在前面先加一再傳值,然後傳入的參數就在
        //當前作用域內+1了 
    //創建根節點的右子樹
        ++(*index);
        CreatBinTree(&((*pRoot)->_pRight), str, size,index,invalid);
         
     }
}
BTNode *CopyBinTree(BTNode *pRoot)//拷貝一棵樹
    
    if(pRoot != NULL)
    {
        
        BTNode * pNewRoot = BuyBTNode(pRoot->_data);
        pNewRoot->_data = pRoot->_data;
        if(pRoot->_pLeft) //判斷是否爲空節省一些時間 
        pNewRoot->_pLeft  = CopyBinTree(pRoot->_pLeft);
        if(pRoot->_pRight) 
        pNewRoot->_pRight = CopyBinTree(pRoot->_pRight);
            //需要接收返回值 
            return pNewRoot;
    }

    
        return NULL;
    }

 void DestroyBinTree(BTNode **pRoot)
{
    if(*pRoot)
    {

    DestroyBinTree(&(*pRoot)->_pLeft);    
    DestroyBinTree(&(*pRoot)->_pRight);
    *pRoot = NULL; 
    free(*pRoot);
    }     
//必須要用後序銷燬(左右根),否則假如用先序銷燬根左右
//先把根銷燬了,再引用根的左右子樹很危險,在c語言中稱爲野指針 
//銷燬前需要先賦值爲NULL 
} 
int * GetBTNodeCount(BTNode *pRoot,int *count)
{
    if(pRoot)
    {
        (*count)++;
        GetBTNodeCount(pRoot->_pLeft,count);
        GetBTNodeCount(pRoot->_pRight,count);
        return count;
    }
    //還有一種return  GetBTNodeCount(pRoot->_pLeft,count)+GetBTNodeCount(pRoot->_pRight,count)+1;
    
} 
 int  GetLeafNodeCount(BTNode *pRoot)
 {
 if(pRoot==NULL)
     return 0;
  if(pRoot!=NULL&&pRoot->_pLeft==NULL&&pRoot->_pRight==NULL)
  {
      return GetLeafNodeCount(pRoot->_pLeft)+GetLeafNodeCount(pRoot->_pRight)+1;
  }
  else
  {
      return GetLeafNodeCount(pRoot->_pLeft)+GetLeafNodeCount(pRoot->_pRight);
  }
 }
 
 int GetKLevelNodeCount(BTNode *pRoot,int k)
 {
     if(pRoot==NULL||k<=0)
     return 0;
     if(k>1)
     {
         return GetKLevelNodeCount(pRoot->_pLeft, k-1)+GetKLevelNodeCount(pRoot->_pRight, k-1);
     }
     if(k == 1)
     {
         return GetKLevelNodeCount(pRoot->_pLeft, k-1)+GetKLevelNodeCount(pRoot->_pRight, k-1)+1;
     }
    /* else
     {
         return 0;不能這麼寫,可能對空指針進行取值操作 
     }*/
 }
 
int Height(BTNode *pRoot) 
{
    if(pRoot==NULL)
    {
        return 0;
    }
    return  Height(pRoot->_pLeft)>Height(pRoot->_pLeft)?Height(pRoot->_pLeft)+1:Height(pRoot->_pLeft)+1;
}
 
 BTNode* LeftChild(BTNode* pNode)
 {
     return NULL!= pNode? pNode->_pLeft:NULL;
 }
 
 BTNode* RightChild(BTNode* pNode)
 {
     return NULL!= pNode? pNode->_pRight:NULL;
 }
 
  BTNode* ParentNode(BTNode* pRoot,BTNode* pNode)
  {
      if(pNode==NULL||pRoot==NULL)
      {
          return 0;
    }
    if(pNode==pRoot->_pLeft||pNode==pRoot->_pRight||pRoot==pNode)
      {
          return pRoot;
    }
    else
    {
        BTNode* root;
        root = ParentNode(pRoot->_pLeft,pNode);
        if(root)
        return root;
        root = ParentNode(pRoot->_pRight,pNode);
        return root;
    }  
      return 0;
  }
 
 void TestBTree()
 {
     char *pStr = "ABD###CE##F";
     BTNode *pRoot = NULL;//要改變pRoot的指向,必須用二級指針 
     int index = 0 ;
     BTNode *pRootC = NULL;
     //控制字符串序號的不能是普通int必須是一級指針 
     CreatBinTree(&pRoot,pStr,strlen(pStr),&index,'#');
     printf("前序遍歷:");
     PreOrder(pRoot);
     printf("\n");
     printf("中序遍歷:");
     InOrder(pRoot);
     printf("\n");
     printf("後序遍歷:");
     PostOrder(pRoot);
     printf("\n");
     pRootC = CopyBinTree(pRoot); 
     printf("前序遍歷:");
     PreOrder(pRootC);
     printf("\n");
     printf("中序遍歷:");
     InOrder(pRootC);
     printf("\n");
     printf("後序遍歷:");
     PostOrder(pRootC);
     printf("\n");
     int count = 0;
     printf("二叉樹的節點個數:    %d\n",*GetBTNodeCount(pRoot,&count));
     printf("二叉樹的葉子節點個數:%d\n",GetLeafNodeCount(pRoot));
     printf("第%d層節點個數爲:     %d\n",3,GetKLevelNodeCount(pRoot, 3));
    printf("二叉樹的深度         %d\n",Height(pRoot)); 
    printf("二叉樹根節點的左孩子的值爲%c\n",LeftChild(pRoot)->_data); 
    printf("二叉樹根節點的右孩子的值爲%c\n",RightChild(pRoot)->_data); 
    if(ParentNode(pRoot->_pLeft,pRoot->_pRight->_pRight))
    printf("二叉樹的雙親節點爲        %c\n",ParentNode(pRoot->_pLeft,pRoot->_pRight->_pRight)->_data);
    else
    printf("找不到雙親節點");//注意不能對空節點進行取值操作 
     //strlen(pStr)爲pStr長度; 
 }
 int main()
 {
     TestBTree();
 }

最後值得注意的一點是,如果只是讀取二叉樹的數據進行遍歷等操作只需要一級指針就可以了,因爲操作的是指向的信息

但是如果是對樹的結構進行修改如建立一棵二叉樹,需要使用二級指針來修改一級指針指向的對象

如果傳參用的是一級指針,確實可以在函數內改變傳入的形參的指向內容,但是形參的指針和實參不是一個指針,只是指向了相同的信息,修改形參不會對實參造成影響。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章