二叉樹前序、中序、後序遍歷相互求法
0.二叉樹相關的基本概念和性質:
定義:
1、滿二叉樹:一棵深度為k且有2的k次方減1個結點的二叉樹稱為滿二叉樹
2、完全二叉樹:如果有深度為k的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應時,稱之為完全二叉樹。
性質:
1、二叉樹的第i層上至多有2的i-1次方個結點(i>=1)。
2、深度為k的二叉樹至多有2的k次方減1個結點(k>=1)。
3、對任何一棵二叉樹T,如果其終端結點數為n0,度為2的結點數為n2,則n0=n2+1。
4、具有n個結點的完全二叉樹的深度為以2為底n的對數取下限加1。
5、如果對一棵有n個結點的完全二叉樹的結點按層序編號,則對任一結點
(1)如果i=1,則結點i是二叉樹的根,無雙親;如果i>1,則雙親PARENT(i)是結點[i/2]
(2)如果2i>n,則結點i無左孩子(結點i為葉子結點);否則其左孩子LCHILD(i)是結點2i
(3)如果2i+1>n,則結點i無右孩子;否則其右孩子RCHILD(i)是結點2i+1.
儲存結構:
順序儲存結構(陣列方式),鏈式儲存結構(二叉連結串列)
二叉樹連結串列儲存方式程式碼實現如下:
struct BiNode
{ Type data;
structBiNode *lchild,*rchild;//左右孩子指標
}BiNode,*BiTree
一.層序遍歷
對二叉樹層序遍歷是指,同層次的節點按照從左到右的順序依次遍歷節點,當一層遍歷結束後進入下一層,直到所有的節點遍歷完成,其實現思想利用一個佇列,儲存每一層的左右孩子節點,進入下一層後依次出隊遍歷上一層儲存的節點同時對其左右孩子進行入隊,通過這種方式迴圈遍歷直到結束。程式碼如下:
void levelTravel(TreeNode *root){
if(root==NULL)
return;
queue<TreeNode *> thequeue;
cout<<root->element<<endl;
if(root->left!=NULL)
thequeue.push(root->left);
if(root->right!=NULL)
thequeue.push(root->right);
while(!thequeue.empty()){
cout<<thequeue.front()->element<<endl;
thequeue.pop();
if(root->left!=NULL)
thequeue.push(root->left);
if(root->right!=NULL)
thequeue.push(root->right);
}
}
二.深度遍歷
二叉樹的深度遍歷包括前序、中序、後序遍歷,三種遍歷方法根據遍歷節點的順序不同進行區分,其遍歷順序分別如下:前序遍歷:
1.訪問根節點
2.前序遍歷左子樹
3.前序遍歷右子樹
程式碼如下:
void preorder(TreeNode*root)
{
TreeNode *p1=root;
TreeNode *p2=p1;
if(p1!=NULL)
{
cout<<p1->val<<endl;
midorder(p1->left);
midorder(p2->right);
}
}
中序遍歷:
1.中序遍歷左子樹
2.訪問根節點
3.中序遍歷右子樹
程式碼如下:
voidmidorder(TreeNode *root)
{
TreeNode *p1=root;
TreeNode *p2=p1;
if(p1!=NULL)
{
midorder(p1->left);
cout<<p1->val<<endl;
midorder(p2->right);
}
}
後序遍歷:
1.後序遍歷左子樹
2.後序遍歷右子樹
3.訪問根節點
程式碼如下:
voidmidorder(TreeNode *root)
{
TreeNode *p1=root;
TreeNode *p2=p1;
if(p1!=NULL)
{
midorder(p1->left);
cout<<p1->val<<endl;
midorder(p2->right);
}
}
對二叉樹進行深度遍歷採用遞迴的方式,程式碼十分整潔,十分有規律。非遞迴的遍歷方式也可以實現,有機會可以自己敲一下,相對比較複雜。
1、已知前序、中序遍歷,求後序遍歷
例:
前序遍歷: GDAFEMHZ
中序遍歷: ADEFGHMZ
畫樹求法:第一步,根據前序遍歷的特點,我們知道根結點為G
第二步,觀察中序遍歷ADEFGHMZ。其中root節點G左側的ADEF必然是root的左子樹,G右側的HMZ必然是root的右子樹。
第三步,觀察左子樹ADEF,左子樹的中的根節點必然是大樹的root的leftchild。在前序遍歷中,大樹的root的leftchild位於root之後,所以左子樹的根節點為D。
第四步,同樣的道理,root的右子樹節點HMZ中的根節點也可以通過前序遍歷求得。在前序遍歷中,一定是先把root和root的所有左子樹節點遍歷完之後才會遍歷右子樹,並且遍歷的左子樹的第一個節點就是左子樹的根節點。同理,遍歷的右子樹的第一個節點就是右子樹的根節點。
第五步,觀察發現,上面的過程是遞迴的。先找到當前樹的根節點,然後劃分為左子樹,右子樹,然後進入左子樹重複上面的過程,然後進入右子樹重複上面的過程。最後就可以還原一棵樹了。該步遞迴的過程可以簡潔表達如下:
1 確定根,確定左子樹,確定右子樹。
2 在左子樹中遞迴。
3 在右子樹中遞迴。
4 列印當前根。
那麼,我們可以畫出這個二叉樹的形狀:
那麼,根據後序的遍歷規則,我們可以知道,後序遍歷順序為:AEFDHZMG
程式設計求法:(依據上面的思路,寫遞迴程式)
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 5 struct TreeNode 6 { 7 struct TreeNode* left; 8 struct TreeNode* right; 9 char elem; 10 }; 11 12 void BinaryTreeFromOrderings(char* inorder, char* preorder, int length) 13 { 14 if(length == 0) 15 { 16 //cout<<"invalid length"; 17 return; 18 } 19 TreeNode* node = new TreeNode;//Noice that [new] should be written out. 20 node->elem = *preorder; 21 int rootIndex = 0; 22 for(;rootIndex < length; rootIndex++) 23 { 24 if(inorder[rootIndex] == *preorder) 25 break; 26 } 27 //Left 28 BinaryTreeFromOrderings(inorder, preorder +1, rootIndex); 29 //Right 30 BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1)); 31 cout<<node->elem<<endl; 32 return; 33 } 34 35 36 int main(int argc, char* argv[]) 37 { 38 printf("Hello World!\n"); 39 char* pr="GDAFEMHZ"; 40 char* in="ADEFGHMZ"; 41 42 BinaryTreeFromOrderings(in, pr, 8); 43 44 printf("\n"); 45 return 0; 46 }
輸出的結果為:AEFDHZMG
2、已知中序和後序遍歷,求前序遍歷
依然是上面的題,這次我們只給出中序和後序遍歷:
中序遍歷: ADEFGHMZ
後序遍歷: AEFDHZMG
畫樹求法:第一步,根據後序遍歷的特點,我們知道後序遍歷最後一個結點即為根結點,即根結點為G。
第二步,觀察中序遍歷ADEFGHMZ。其中root節點G左側的ADEF必然是root的左子樹,G右側的HMZ必然是root的右子樹。
第三步,觀察左子樹ADEF,左子樹的中的根節點必然是大樹的root的leftchild。在前序遍歷中,大樹的root的leftchild位於root之後,所以左子樹的根節點為D。
第四步,同樣的道理,root的右子樹節點HMZ中的根節點也可以通過前序遍歷求得。在前後序遍歷中,一定是先把root和root的所有左子樹節點遍歷完之後才會遍歷右子樹,並且遍歷的左子樹的第一個節點就是左子樹的根節點。同理,遍歷的右子樹的第一個節點就是右子樹的根節點。
第五步,觀察發現,上面的過程是遞迴的。先找到當前樹的根節點,然後劃分為左子樹,右子樹,然後進入左子樹重複上面的過程,然後進入右子樹重複上面的過程。最後就可以還原一棵樹了。該步遞迴的過程可以簡潔表達如下:
1 確定根,確定左子樹,確定右子樹。
2 在左子樹中遞迴。
3 在右子樹中遞迴。
4 列印當前根。
這樣,我們就可以畫出二叉樹的形狀,如上圖所示,這裡就不再贅述。
那麼,前序遍歷: GDAFEMHZ
程式設計求法:(並且驗證我們的結果是否正確)
#include <iostream> #include <fstream> #include <string> struct TreeNode { struct TreeNode* left; struct TreeNode* right; char elem; }; TreeNode* BinaryTreeFromOrderings(char* inorder, char* aftorder, int length) { if(length == 0) { return NULL; } TreeNode* node = new TreeNode;//Noice that [new] should be written out. node->elem = *(aftorder+length-1); std::cout<<node->elem<<std::endl; int rootIndex = 0; for(;rootIndex < length; rootIndex++)//a variation of the loop { if(inorder[rootIndex] == *(aftorder+length-1)) break; } node->left = BinaryTreeFromOrderings(inorder, aftorder , rootIndex); node->right = BinaryTreeFromOrderings(inorder + rootIndex + 1, aftorder + rootIndex , length - (rootIndex + 1)); return node; } int main(int argc, char** argv) { char* af="AEFDHZMG"; char* in="ADEFGHMZ"; BinaryTreeFromOrderings(in, af, 8); printf("\n"); return 0; }
輸出結果:GDAFEMHZ