前言:存儲二叉樹的關鍵是如何表示結點之間的邏輯關係,也就是雙親和孩子之間的關係。在具體應用中,可能要求從任一結點能直接訪問到它的孩子。ios
1、二叉鏈表算法
二叉樹通常多采用二叉鏈表(binary linked list)存儲,其基本思想是:令二叉樹的每個結點對應一個鏈表結點鏈表結點除了存放與二叉樹結點有關的數據信息外,還要設置指示左右孩子的指針。二叉鏈表的結點結構以下圖所示:數據結構
lchild | data | rchild |
其中,data爲數據域,存放該結點的數據信息;函數
lchild爲左指針域,存放指向左孩子的指針,當左孩子不存在時爲空指針;spa
rchild爲右指針域,存放指向右孩子的指針,當右孩子不存在時爲空指針;指針
能夠用C++語言中的結構體類型描述二叉鏈表的結點,因爲二叉鏈表的結點類型不肯定,因此採用C++的模板機制。以下:code
1 // 二叉鏈表的節點 2 template<class T> 3 struct BiNode 4 { 5 T data; // 數據域 6 BiNode<T>*lchild, *rchild; // 左右指針域 7 };
2、C++實現blog
將二叉樹的二叉鏈表存儲結構用C++的類實現。爲了不類的調用者訪問BiTree類的私有變量root,在構造函數、析構函數以及遍歷函數中調用了相應的私有函數。遞歸
具體代碼實現以下:隊列
一、頭文件「cirqueue.h」
此頭文件爲隊列的類實現,層序遍歷要用到隊列,因此本身定義了一個隊列。
1 #pragma once 2 #include <iostream> 3 const int queueSize = 100; 4 template<class T> 5 class queue 6 { 7 public: 8 .... 9 T data[queueSize]; 10 int front, rear; 11 .... 12 };
二、頭文件「bitree.h」
此頭文件爲二叉鏈表的類實現。
#pragma once #include <iostream> #include "cirqueue.h" // 二叉鏈表的節點 template<class T> struct BiNode { T data; // 數據域 BiNode<T>*lchild, *rchild; // 左右指針域 }; // 二叉鏈表類實現 template<class T> class BiTree { public: BiTree() { root = Creat(root); } // 構造函數,創建一顆二叉樹 ~BiTree() { Release(root); } // 析構函數,釋放各節點的存儲空間 void PreOrder() { PreOrder(root); } // 遞歸前序遍歷二叉樹 void InOrder() { InOrder(root); } // 遞歸中序遍歷二叉樹 void PostOrder() { PostOrder(root); } // 遞歸後序遍歷二叉樹 void LeverOrder(); // 層序遍歷二叉樹 private: BiNode<T>* root; // 指向根節點的頭節點 BiNode<T>* Creat(BiNode<T>* bt); // 構造函數調用 void Release(BiNode<T>* bt); // 析構函數調用 void PreOrder(BiNode<T>* bt); // 前序遍歷函數調用 void InOrder(BiNode<T>* bt); // 中序遍歷函數調用 void PostOrder(BiNode<T>* bt); // 後序遍歷函數調用 }; template<class T> inline void BiTree<T>::LeverOrder() { queue<BiNode<T>*> Q; // 定義一個隊列 Q.front = Q.rear = -1; // 順序隊列 if (root == NULL) return; Q.data[++Q.rear] = root; // 根指針入隊 while (Q.front != Q.rear) { BiNode<T>* q = Q.data[++Q.front]; // 出隊 cout << q->data; if (q->lchild != NULL) Q.data[++Q.rear] = q->lchild; // 左孩子入隊 if (q->rchild != NULL) Q.data[++Q.rear] = q->rchild; // 右孩子入隊 } } template<class T> inline BiNode<T>* BiTree<T>::Creat(BiNode<T>* bt) { T ch; cin >> ch; // 輸入結點的數據信息,假設爲字符 if (ch == '#') // 創建一棵空樹 bt = NULL; else { bt = new BiNode<T>; // 生成一個結點,數據域爲ch bt->data = ch; bt->lchild = Creat(bt->lchild); // 遞歸創建左子樹 bt->rchild = Creat(bt->rchild); // 遞歸創建右子樹 } return bt; } template<class T> inline void BiTree<T>::Release(BiNode<T>* bt) { if (bt != NULL) { Release(bt->lchild); // 釋放左子樹 Release(bt->rchild); // 釋放右子樹 delete bt; // 釋放根節點 } } template<class T> inline void BiTree<T>::PreOrder(BiNode<T>* bt) { if (bt == NULL) // 遞歸調用的結束條件 return; cout << bt->data; // 訪問根節點bt的數據域 PreOrder(bt->lchild); // 前序遞歸遍歷bt的左子樹 PreOrder(bt->rchild); // 前序遞歸遍歷bt的右子樹 } template<class T> inline void BiTree<T>::InOrder(BiNode<T>* bt) { if (bt == NULL) return; InOrder(bt->lchild); cout << bt->data; InOrder(bt->rchild); } template<class T> inline void BiTree<T>::PostOrder(BiNode<T>* bt) { if (bt == NULL) return; PostOrder(bt->lchild); PostOrder(bt->rchild); cout << bt->data; }
說明:一、除了層序遍歷,其餘遍歷均爲遞歸算法。
二、爲何層序遍歷使用隊列:在進行層序遍歷時,對某一層的結點訪問完後,再按照它們的訪問次序對各個結點的左孩子和右孩子順序訪問,這樣一層一層進行,先訪問的結點其左右孩子也要先訪問,這符合隊列的操做特性,所以,在進行層序遍歷時,可設置一個隊列存放已訪問的結點。
三、構造函數對二叉樹的特殊處理:將二叉樹中每一個結點的空指針引出一個虛結點,其值爲一特定值,如‘#’,以標識其爲空。
四、二叉鏈表屬於動態內存分配,須要在析構函數中釋放二叉鏈表的全部結點。在釋放某結點時,該結點的左右都子樹已經釋放,因此應該採用後序遍歷。
三、主函數
1 #include"bitree.h" 2 using namespace std; 3 4 int main() 5 { 6 BiTree<char>* bitree=new BiTree<char>(); // 建立一棵二叉樹 7 bitree->PreOrder(); // 前序遍歷 8 cout << endl; 9 bitree->InOrder(); // 中序遍歷 10 cout << endl; 11 bitree->PostOrder(); // 後序遍歷 12 cout << endl; 13 bitree->LeverOrder(); // 層序遍歷 14 delete bitree; 15 16 system("pause"); 17 return 0; 18 }
3、實例
創建以下二叉樹,並輸出四種遍歷的結果。
運行結果:
結果正確。
參考文獻:
[1]王紅梅, 胡明, 王濤. 數據結構(C++版)[M]. 北京:清華大學出版社。
立刻元旦了,祝你們元旦快樂!!2017-12-29