二叉樹的基本功能實現方法(C++)


假設:有一個n個元素的完全二叉樹,為了使其成為滿二叉樹,補全沒有孩子的節點是的除了葉節點所有節點都有兩個孩子,即最低層皆為-1.

例1:

              1

         2          3  

      4     5     -1    6 

     -1  -1     -1  -1                -1   -1   

 

補全的節點賦值為-1,表示當前無節點,需轉向別的子樹。

 

step 1:首先,對於一棵二叉樹,需定義一個節點的類模板:

包括:節點鍵值、左子樹指針、右子樹指針

template <typename T>
class binaryTreeNode {
public:
    T element;
    binaryTreeNode<T>* leftChild;
    binaryTreeNode<T>* rightChild;
    
    binaryTreeNode(){leftChild = NULL;rightChild = NULL;}
};

step 2:如何創建一棵二叉樹呢?根據鏈表的特性,成員變量為:節點指針類型的mRoot根節點。除此之外,還包括常見的成員函數:如 獲取樹的當前規模、獲取樹的深度、打印或輸出樹及銷毀二叉樹等操作。

 1 template <typename T>
 2 class binaryTree {
 3 private:
 4     binaryTreeNode<T>* mRoot;//樹根
 5     
 6     
 7     int getSize(binaryTreeNode<T>*);
 8     int getHeight(binaryTreeNode<T>*);
 9     
10     void preOrder(binaryTreeNode<T>*);
11     void inOrder(binaryTreeNode<T>*);
12     void postOrder(binaryTreeNode<T>*);
13     void distroy(binaryTreeNode<T>*&);
14     binaryTreeNode<T>* AddNode(const T& key,int direction,binaryTreeNode<T>*& root);
15     
16 public:
17     binaryTree();
18     virtual ~binaryTree();
19     binaryTreeNode<T>* Create();//遞歸的創建二叉樹的節點
20     void AddNode(const T& key,int direction);
21     
22     int getSize();//遞歸得到樹的節點數目
23     int getHeight();//遞歸得到樹的高度
24     
25     //遞歸遍歷
26     void preOrder();//前序遍歷
27     void inOrder();//中序遍歷
28     void postOrder();//后序遍歷
29     
30     //刪除二叉樹
31     void distroy();
32     
33 };
View Code

 

>>首先,怎么在二叉樹中插入節點呢?
可以想到的是,插入新節點的時候需要標明 插得是左孩子還是右孩子which,在哪一個父節點下插入where,及插入的鍵值是多少what,簡單的來說,就是WWW問題

 1 template <typename T>
 2 binaryTreeNode<T>* binaryTree<T>::AddNode(const T& key,int direction,binaryTreeNode<T>*& root)
 3 {
 4     if(direction == 0)//左孩子
 5     {
 6         if(root->leftChild == NULL){//找到對應的葉節點插入
 7             root->leftChild = new binaryTreeNode<T>(key);
 8         }
 9         else{
10             root->leftChild = AddNode(key, direction, root->leftChild);
11         }
12     }
13     
14     else//右孩子
15     {
16         if (root->rightChild == NULL) {//找到相應的葉節點插入
17             root->rightChild = new binaryTreeNode<T>(key);
18         }
19         else{
20             root->rightChild = AddNode(key, direction, root->rightChild);
21         }
22     }
23     
24     return root;
25 }
26 
27 template <typename T>
28 void binaryTree<T>::AddNode(const T& key,int direction)
29 {
30     AddNode(key, direction, mRoot);
31 }

 

了解了這個思路后,可以不用一個個插入節點,用輸入流的方式直接創建一棵樹,如下程序:

 1 template <typename T>
 2 binaryTreeNode<T>* binaryTree<T>::Create(){
 3     
 4     binaryTreeNode<T>* current = NULL;
 5     
 6     T val;
 7     
 8     cin >> val;//輸入鍵值
 9     
10     if(val == -1)//標識當前子樹為空,轉向下一節點
11     {
12         return NULL;
13     }
14     
15     else{//遞歸的創建左右子樹
16         current = new binaryTreeNode<T>;
17         current->element = val;
18         current->leftChild = Create();
19         current->rightChild = Create();
20         return current;
21     }
22 }

可以發現,-1是一個過渡標識,標明當前從遞歸左子樹 轉向 遞歸右子樹。而上述創建程序是一個前序遍歷,所謂前序遍歷是指:

  1.先訪問父節點

  2.遞歸左子樹

  3.遞歸右子樹

時間復雜度是O(N),因為遍歷了每一個節點。

 

>>創建了二叉樹后,怎么銷毀?其實只要一一刪除每個節點即可,考慮到鏈表結構,我們不能使用下標去刪除節點,只能一個個的訪問,而二叉樹典型的遍歷方法有:前序遍歷、中序遍歷 及 后序遍歷。在這里,我們使用后序遍歷進行遞歸刪除, 即自下而上的刪除。

 1 /*二叉樹的銷毀操作:后序遍歷刪除
 2 
 3 1)不能使用該聲明:void distroy(binaryTreeNode<T>* pNode);該聲明會創建一個局部的臨時對象來保存傳遞的指針
 4 雖然實參指針和局部指針都執行同一塊堆空間,delete局部指針也會刪除二叉樹結構所占用的堆內存
 5 但是實參指針將出現無所指的狀態,出現不可預料的錯誤
 6 因此傳遞的是指針的引用,這樣才能將實參指針置空。
 7 
 8 2)使用遞歸方法釋放節點
 9  
10 */
11 
12 template <typename T>
13 void binaryTree<T>::distroy(binaryTreeNode<T>*& pNode)
14 {
15     if(pNode)
16     {
17         distroy(pNode->leftChild);
18         distroy(pNode->rightChild);
19         delete pNode;
20         pNode = NULL;
21     }
22 }
23 template <typename T>
24 void binaryTree<T>::distroy()
25 {
26     distroy(mRoot);
27 }

如要刪除如上例1中的二叉樹,刪除過程依次為:4 5 2 6 3 1

 

>>對於獲取樹的深度,有一種方法是,獲取左右子樹的深度,比較子樹深度大小,大的那個增1即為樹的深度了。當然,也是遞歸實現。

 1 template <typename T>
 2 int binaryTree<T>::getHeight()
 3 {
 4     return getHeight(mRoot);
 5 }
 6 /*
 7 獲取當前節點的深度
 8  遞歸的方法首先要設置截止條件,在進行遞歸操作。
 9  0.約束條件:節點為空
10  1.遞歸左子樹,每次遞歸加1
11  2.遞歸右子樹,每次遞歸加1
12  3.比較左右子樹深度,更深的子樹+1即為當前節點深度。
13 */
14 
15 template <typename T>
16 int binaryTree<T>::getHeight(binaryTreeNode<T>* node)
17 {
18     if(node == NULL)
19         return 0;
20     else{
21         int depL = getHeight(node->leftChild);
22         int depR = getHeight(node->rightChild);
23         return (depL > depR) ? depL+1 : depR+1;
24     }
25         
26 }

 

>>同理,獲取樹的規模只要遍歷整棵樹即可,這里用遞歸實現。這里僅給出前序遍歷,后序遍歷和中序遍歷類似則不再給出。

 1 template <typename T>
 2 void binaryTree<T>::preOrder()
 3 {
 4     cout <<"前序遍歷: ";
 5     preOrder(mRoot);
 6     cout << endl;
 7 }
 8 
 9 /*
10  前序遍歷:
11  1.由於是遞歸實現,所以要設置截止條件:當前節點為空
12  2.先訪問父節點,再訪問左節點,最后訪問右孩子
13  
14  */
15 template <typename T>
16 void binaryTree<T>::preOrder(binaryTreeNode<T>* node)
17 {
18     if(node == NULL)
19         return;
20     else{
21         cout << node->element <<' ';
22         preOrder(node->leftChild);
23         preOrder(node->rightChild);
24     }
25 }

 

對於例1的遍歷結果,如下:

輸入:1 2 4 -1 -1 5 -1 -1 3 -1 6 -1 -1
前序遍歷: 1 2 4 5 3 6  
中序遍歷: 4 2 5 1 3 6  
后序遍歷: 4 5 2 6 3 1 
樹的高度為: 3
樹的節點數目: 6

 

>>總之呢,創建二叉樹的全過程都用到了遞歸,那么遞歸到底是什么呢?

從定義上來講:遞歸作為一種算法,是讓函數/子程序/過程在程序運行過程中調用自身的方法,能夠把一個較為復雜的問題經過層層轉換,得到一個與原問題相似但是規模大大減小的問題來求解。遞歸方法大大減少了代碼的復雜度。

實現方法:首先遞歸必須設置一個終止條件,當滿足終止條件時,則遞歸返回。除此之外,則遞歸調用自身。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM