假設:有一個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 };
>>首先,怎么在二叉樹中插入節點呢?
可以想到的是,插入新節點的時候需要標明 插得是左孩子還是右孩子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
>>總之呢,創建二叉樹的全過程都用到了遞歸,那么遞歸到底是什么呢?
從定義上來講:遞歸作為一種算法,是讓函數/子程序/過程在程序運行過程中調用自身的方法,能夠把一個較為復雜的問題經過層層轉換,得到一個與原問題相似但是規模大大減小的問題來求解。遞歸方法大大減少了代碼的復雜度。
實現方法:首先遞歸必須設置一個終止條件,當滿足終止條件時,則遞歸返回。除此之外,則遞歸調用自身。