二叉树性质及遍历
一、二叉樹的定義
樹的每個結點至多只有二棵子樹(不存在度大于2的結點),樹的子樹有左右之分,次序不能顛倒。
二、二叉樹的性質
(1) 在非空二叉樹中,第i層的結點總數不超過?, i>=1; (2) 深度為h的二叉樹最多有?個結點(h>=1),最少有h個結點; (3) 對于任意一棵二叉樹,如果其葉結點數為N0,而度數為2的結點總數為N2,則N0=N2+1; (4) 具有n個結點的完全二叉樹的深度為 (5)有N個結點的完全二叉樹各結點如果用順序方式存儲,則結點之間有如下關系: 若I為結點編號則 如果I>1,則其父結點的編號為I/2; 如果2*I<=N,則其左兒子(即左子樹的根結點)的編號為2*I;若2*I>N,則無左兒子; 如果2*I+1<=N,則其右兒子的結點編號為2*I+1;若2*I+1>N,則無右兒子。 (6)給定N個節點,能構成h(N)種不同的二叉樹。 h(N)為卡特蘭數的第N項。h(n)=C(2*n,n)/(n+1)。三、二叉樹的遍歷
二叉樹的遍歷順序分為三種:先序遍歷、中序遍歷和后序遍歷。而遍歷方式又分為遞歸遍歷和非遞歸遍歷。1.先序遍歷
前序遍歷按照“根結點-左孩子-右孩子”的順序進行訪問。
a.遞歸實現:
1 void preOrder1(BinTree *root) //遞歸前序遍歷 2 { 3 if(root!=NULL) 4 { 5 cout<<root->data<<" "; 6 preOrder1(root->lchild); 7 preOrder1(root->rchild); 8 } 9 }b.非遞歸實現:
根據前序遍歷訪問的順序,優先訪問根結點,然后再分別訪問左孩子和右孩子。即對于任一結點,其可看做是根結點,因此可以直接訪問,訪問完之后,若其左孩子不為空,按相同規則訪問它的左子樹;當訪問其左子樹時,再訪問它的右子樹。因此其處理過程如下:
???? 對于任一結點P:
???? 1)訪問結點P,并將結點P入棧;
?????2)判斷結點P的左孩子是否為空,若為空,則取棧頂結點并進行出棧操作,并將棧頂結點的右孩子置為當前的結點P,循環至1);若不為空,則將P的左孩子置為當前的結點P;
???? 3)直到P為NULL并且棧為空,則遍歷結束。
1 void preOrder2(BinTree *root) //非遞歸前序遍歷 2 { 3 stack<BinTree*> s; 4 BinTree *p=root; 5 while(p!=NULL||!s.empty()) 6 { 7 while(p!=NULL) 8 { 9 cout<<p->data<<" "; 10 s.push(p); 11 p=p->lchild; 12 } 13 if(!s.empty()) 14 { 15 p=s.top(); 16 s.pop(); 17 p=p->rchild; 18 } 19 } 20 }2.中序遍歷
中序遍歷按照“左孩子-根結點-右孩子”的順序進行訪問。
a.遞歸實現
1 void inOrder1(BinTree *root) //遞歸中序遍歷 2 { 3 if(root!=NULL) 4 { 5 inOrder1(root->lchild); 6 cout<<root->data<<" "; 7 inOrder1(root->rchild); 8 } 9 }b.非遞歸實現
根據中序遍歷的順序,對于任一結點,優先訪問其左孩子,而左孩子結點又可以看做一根結點,然后繼續訪問其左孩子結點,直到遇到左孩子結點為空的結點才進行訪問,然后按相同的規則訪問其右子樹。因此其處理過程如下:
???對于任一結點P,
? 1)若其左孩子不為空,則將P入棧并將P的左孩子置為當前的P,然后對當前結點P再進行相同的處理;
??2)若其左孩子為空,則取棧頂元素并進行出棧操作,訪問該棧頂結點,然后將當前的P置為棧頂結點的右孩子;
? 3)直到P為NULL并且棧為空則遍歷結束
1 void inOrder2(BinTree *root) //非遞歸中序遍歷 2 { 3 stack<BinTree*> s; 4 BinTree *p=root; 5 while(p!=NULL||!s.empty()) 6 { 7 while(p!=NULL) 8 { 9 s.push(p); 10 p=p->lchild; 11 } 12 if(!s.empty()) 13 { 14 p=s.top(); 15 cout<<p->data<<" "; 16 s.pop(); 17 p=p->rchild; 18 } 19 } 20 }3.后序遍歷
后序遍歷按照“左孩子-右孩子-根結點”的順序進行訪問。
a.遞歸實現
1 void postOrder1(BinTree *root) //遞歸后序遍歷 2 { 3 if(root!=NULL) 4 { 5 postOrder1(root->lchild); 6 postOrder1(root->rchild); 7 cout<<root->data<<" "; 8 } 9 }b.非遞歸實現
后序遍歷的非遞歸實現是三種遍歷方式中最難的一種。因為在后序遍歷中,要保證左孩子和右孩子都已被訪問并且左孩子在右孩子前訪問才能訪問根結點,這就為流程的控制帶來了難題。
要保證根結點在左孩子和右孩子訪問之后才能訪問,因此對于任一結點P,先將其入棧。如果P不存在左孩子和右孩子,則可以直接訪問它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。若非上述兩種情況,則將P的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。
1 void postOrder3(BinTree *root) //非遞歸后序遍歷 2 { 3 stack<BinTree*> s; 4 BinTree *cur; //當前結點 5 BinTree *pre=NULL; //前一次訪問的結點 6 s.push(root); 7 while(!s.empty()) 8 { 9 cur=s.top(); 10 if((cur->lchild==NULL&&cur->rchild==NULL)|| 11 (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild))) 12 { 13 cout<<cur->data<<" "; //如果當前結點沒有孩子結點或者孩子節點都已被訪問過 14 s.pop(); 15 pre=cur; 16 } 17 else 18 { 19 if(cur->rchild!=NULL) 20 s.push(cur->rchild); 21 if(cur->lchild!=NULL) 22 s.push(cur->lchild); 23 } 24 } 25 }四、參考資料
http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html
?
轉載于:https://www.cnblogs.com/wangjzh/p/4695700.html
總結
- 上一篇: Android自定义xml解析
- 下一篇: CentOS6 下Vim安装和配置