1. 程式人生 > >C++ 二叉樹的實現、基本操作以及指標使用注意事項(轉自部落格)

C++ 二叉樹的實現、基本操作以及指標使用注意事項(轉自部落格)

內容:

  • 模板實現簡單的二叉樹
  • 二叉樹的前序,中序,後序遍歷
  • 統計二叉樹結點的個數和深度
  • 二叉樹的銷燬操作
  •  

具體的實現過程及注意事項見程式碼部分;

 

#include <iostream>

using namespace std;



//*************************************************************************************

//二叉樹結點類的定義

template<class T>                  //模版結構體

struct TreeNode

{

    T data;                       //節點的內容

    TreeNode <T> *Lchild,*Rchild; //節點的左子樹和右子樹



    //可選擇引數的預設建構函式

    /*TreeNode(T nodeValue = T(),TreeNode<T> *leftNode = NULL,TreeNode<T> *rightNode = NULL )

        :data(nodeValue),Lchild(leftNode),Rchild(rightNode){} */     

};



//**************************************************************************************

//二叉樹的建立

template <class T> //模版方法

void createBinaryTree(TreeNode<T> *&root )  //傳遞指標的引用

{

    TreeNode<T>* p = root;

    T nodeValue ;

    cin>>nodeValue;

    if(nodeValue==-1)

    {

        root=NULL;

    }

    else

    {

        root=new TreeNode<T>();            //構造一個節點

        root->data = nodeValue;

        createBinaryTree(root->Lchild);    //遞迴構造左子樹

        createBinaryTree(root->Rchild);    //遞迴構造右子樹

    }

}



//************************************************************************************

//二叉樹的先序遍歷

template <class T>

void preOrder( TreeNode<T> * & p) //傳遞指標的引用

{

    if(p)

    {

        cout<<p->data<<" ";

        preOrder(p->Lchild);

        preOrder(p->Rchild);

    }

}



//**************************************************************************************

//二叉樹的中序遍歷

template <class T>

void inOrder(TreeNode<T> * & p) //傳遞指標的引用

{

     

    if(p)

    {

        inOrder(p->Lchild);

        cout<<p->data<<" ";

        inOrder(p->Rchild);

    }

}



//**************************************************************************************

//二叉樹的後序遍歷

template <class T>

void postOrder(TreeNode<T> *& p) //傳遞指標的引用

{

    if(p)

    {

        postOrder(p->Lchild);

        postOrder(p->Rchild);

        cout<<p->data<<" ";

    }

}



//*************************************************************************************

//統計二叉樹中結點的個數

template<class T>

int countNode(TreeNode<T> * & p) //傳遞指標的引用

{

    if(p == NULL) return 0;

    return 1+countNode(p->Lchild)+countNode(p->Rchild);

}



//***********************************************************************************

//求二叉樹的深度

template<class T>

int depth(TreeNode<T> *& p) //傳遞指標的引用

{

    if(p == NULL)

        return -1;

    int h1 = depth(p->Lchild);

    int h2 = depth(p->Rchild);

    if(h1>h2)return (h1+1);

    return h2+1;

}



//***********************************************************************************

//二叉樹的消毀操作

//容易混淆的錯誤宣告:void destroy(TreeNode<T>* p) 這種宣告會建立一個區域性的臨時物件來儲存傳遞的指標

//雖然2個指標都執行同一塊堆空間,delete區域性指標 也會刪除二叉樹結構所佔用的堆記憶體

//但是全域性傳遞的那個指標將會是垃圾指標,會產生不可預料的錯誤

//void destroy(TreeNode<T> *& p) 此函式的引數為全域性指標的一個別名,代表全域性指標rootNode本身

//  這樣p = NULL;能達到置空指標的左右

//可選的方案是在呼叫完destroy方法之後,在主函式中執行rootNode = NULL操作

template<class T>

void destroy(TreeNode<T> *& p)  //傳遞指標的引用,消毀函式,用來消毀二叉樹中的各個結點

{

    if(p)

    {

        //錯誤 return之後 沒有執行delete p

        //return destroy(p->Lchild);

        //return destroy(p->Rchild);



        destroy(p->Lchild);

        destroy(p->Rchild);



        //delete只能釋放由使用者通過new方式在堆中申請的記憶體,

        //是通過變數宣告的方式由系統所宣告的棧記憶體不能使用delete刪除



        //delete和free函式一樣,不修改它引數對應指標指向的內容,也不修改指標本身,

        //只是在堆記憶體管理結構中將指標指向的內容標記為可被重新分配

        delete p;



        //堆上記憶體釋放 棧上指標並不銷燬

        //此時p指向的地址未知,此時執行*p = ? 操作會導致不可預料的錯誤

        //但是可以重新賦值p = &x;

        //最好delete之後把P置空

        p = NULL;



    }

}



//********************************************************************************

//主函式的設計

int main ()

{

    TreeNode<int> * rootNode = NULL;

    int choiced = 0;

    while(true)

    {

        system("cls"); //清屏

        cout<<"\n\n\n                              ---主介面---\n\n\n";

        cout<<"                     1、建立二叉樹                2、先序遍歷二叉樹\n";

        cout<<"                     3、中序遍歷二叉樹            4、後序遍歷二叉樹\n";

        cout<<"                     5、統計結點總數              6、檢視樹深度    \n";

        cout<<"                     7、消毀二叉樹                0、退出\n\n";

        cout<<"             請選擇操作:";

        cin>>choiced;

        if(choiced == 0)

            return 0;

        else if(choiced == 1)

        {

            system("cls");

            cout<<"請輸入每個結點,回車確認,並以-1結束:\n";

            createBinaryTree(rootNode );

        }

        else if(choiced == 2)

        {

            system("cls");

            cout<<"先序遍歷二叉樹結果:\n";

            preOrder(rootNode);

            cout<<endl;

            system("pause"); //暫停螢幕

        }

        else if(choiced == 3)

        {

            system("cls");

            cout<<"中序遍歷二叉樹結果:\n";

            inOrder(rootNode);

            cout<<endl;

            system("pause");

        }

        else if(choiced == 4)

        {

            system("cls");

            cout<<"後序遍歷二叉樹結果:\n";

            postOrder(rootNode);

            cout<<endl;

            system("pause");

        }

        else if(choiced == 5)

        {

            system("cls");

            int count = countNode(rootNode);

            cout<<"二叉樹中結點總數為"<<count<<endl;

            system("pause");

        }

        else if(choiced == 6)

        {

            system("cls");

            int dep = depth(rootNode);

            cout<<"此二叉樹的深度為"<<dep<<endl;

            system("pause");

        }

        else if(choiced == 7)

        {

            system("cls");

            cout<<"二叉樹已被消毀!\n";

            destroy(rootNode);

            cout<<endl;

            system("pause");

        }

        else

        {

            system("cls");

            cout<<"\n\n\n\n\n\t錯誤選擇!\n";

        }

         

    }

}