二叉樹一樣有兩種存儲方式,數組和鏈式存儲,對於數組來講,咱們利用二叉樹的性質而後利用下標能夠方便的找到一個節點的子節點和父節點。node
二叉樹的性質:
1.二叉樹的第i層上至多有2i-1個節點
2.深度爲K的二叉樹至多有2k-1個節點
3.任何一個二叉樹中度數爲2的節點的個數必度數爲0的節點數目少1.
說明:度數爲0,爲葉子節點。
4.具備n個節點的徹底二叉樹的深度爲|_Log2N_|+1
5.若徹底二叉樹中的某節點編號爲i,則如有左孩子編號爲2i,如有右孩子編號爲2i+1,母親節點爲i/2。算法
結合第5條性質:數組
若徹底二叉樹中的某節點編號爲i,則如有左孩子編號爲2i,如有右孩子編號爲2i+1,母親節點爲i/2。數據結構
能夠在這種徹底二叉樹中十分方便的找到任何相關聯(父子、兄弟等)的元素。post
可是因爲順序存儲天生適配於徹底二叉樹,對於下面這種非徹底二叉樹並不合適,主要體如今空間上的浪費,因此咱們須要用到另外一種存儲方式——鏈式存儲。spa
在鏈式存儲中,每一個節點的結構以下3d
結構描述:指針
一個存儲數據的變量與兩個指向孩子的指針域。code
利用指針域咱們即可以完美的存儲非徹底二叉樹,以下:blog
typedef struct BiTNode { TElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree;
建立二叉樹不是很容易的,而且它的建立方式也對應三種順序。
其實二叉樹的遍歷和二叉樹的建立能夠說是幾乎同樣的過程。無非是建立的過程是給data賦值,遍歷是取出data。
咱們先不給出建立的代碼,而是要明白:
說明:
1.葉子節點不是沒有左右孩子,只不過左右孩子都是空。
2.咱們的二叉樹是手動輸入數據進行建立的 我在實際開發中不多是這樣建立的,因此咱們要處理空節點的問題。
3.建立和遍歷的執行順序(先序、中序、後序)是同樣的,那麼輸入和取出的值纔會是同樣的。
好比咱們要先序建立一個上述圖片中的二叉樹,咱們應該怎樣輸入呢?
AB##CD##EF##G##
# 在這裏是表示葉子節點的左或右節點爲空。
代碼以下:
void createBitree(Bitree &T) { char ch; if((ch=getchar())=='#') T=NULL; else { T=(Bitnode*)malloc(sizeof(Bitnode)); T->data=ch; createBitree(T->Lchild); createBitree(T->Rchild); } }
/*先序遍歷*/ void preTraverse(Bitree T) { if(T!=NULL) { printf("%c",T->data); preTraverse(T->Lchild); preTraverse(T->Rchild); } }
/*中序遍歷*/ void inorder(Bitree T) { if(T!=NULL) { inorder(T->Lchild); printf("%c",T->data); inorder(T->Rchild); } }
/*後序遍歷*/ void postorder(Bitree T) { if(T!=NULL) { postorder(T->lchild); postorder(T->rchild); printf("%c",T->data); } }
►咱們輸入字符後三種遍歷狀況以下:
AB##CD##EF##G##
先序:ABCDEFG
中序:BADCFEG
後序:BDFGECA
int Depth(Bitree T) {//返回深度 int d,dl,dr; if(!T) d=0; else { dl=Depth(T->Lchild); dr=Depth(T->Rchild); d=1+(dl>dr?dl:dr) ; } return d; }
queue<Bitree> TreeQueue; //使用隊列 TreeQueue.push(tree); //先將隊頭元素加入隊列 Bitree p = tree; while (!TreeQueue.empty()) //循環判斷隊列是否未空,若不空則 { p = TreeQueue.front(); //獲取隊列頭節點,並出隊列 TreeQueue.pop(); printf(" %c ", p->data); //打印隊列元素 if (p->Lchild) //若是該節點有左節點 { TreeQueue.push(p->Lchild); //加入隊列 } if (p->Rchild) //若是該節點有右節點 { TreeQueue.push(p->Rchild); //加入隊列 } }
|說明:
1.算法軌跡:
依舊使用下圖:
演示:
咱們首先將A加入隊列, 此時對列 中只有 ⇐[A]
咱們將A出彈出隊列,並將它的左右子樹 BC加入隊列,此時隊列中有 ⇐[BC] ,打印出 A
咱們將B出彈出隊列,它沒有子樹,咱們不作任何操做,此時隊列中有 ⇐[C] ,打印出 ABC
咱們將C出彈出隊列,並將它的左右子樹 DE加入隊列,此時隊列中有 ⇐[DE] ,打印出 ABC
咱們將D出彈出隊列,它沒有子樹,咱們不作任何操做,此時隊列中有 ⇐[E] ,打印出 ABCD
咱們將E出彈出隊列,並將它的左右子樹 FG加入隊列,此時隊列中有 ⇐[FG] ,打印出 ABCDE
咱們將F出彈出隊列,它沒有子樹,咱們不作任何操做,此時隊列中有 ⇐[G] ,打印出 ABCDEF
咱們將G出彈出隊列,它沒有子樹,咱們不作任何操做,此時隊列中有 ⇐[null] ,打印出 ABCDEFG
結論:
根據軌跡咱們不難發現,隊列是保證層序遍歷的基礎,由於它保證了先加入隊列的上層元素會必後加入隊列的下層元素優先出隊列。
2.這段代碼直接使用會出現問題,由於咱們使用了C++標準模板庫中的queue。因此,咱們須要加入頭文件
#include<queue>,而且要使用命名空間 using namespace std;。
3.咱們對待數據結構最重要的是理解和使用,實現它只是幫助咱們理解,咱們無須重複造輪子,在之後的算法中,咱們將盡量的引用標準模板庫。