二叉樹知道前序和中序求後序,知道中序後序求中序
今天來總結下二叉樹前序、中序、後序遍歷相互求法,即如果知道兩個的遍歷,如何求第三種遍歷方法,比較笨的方法是畫出來二叉樹,然後根據各種遍歷不同的特性來求,也可以程式設計求出,下面我們分別說明。
首先,我們看看前序、中序、後序遍歷的特性:
前序遍歷:
1.訪問根節點
2.前序遍歷左子樹
3.前序遍歷右子樹
中序遍歷:
1.中序遍歷左子樹
2.訪問根節點
3.中序遍歷右子樹
後序遍歷:
1.後序遍歷左子樹
2.後序遍歷右子樹
3.訪問根節點
一、已知前序、中序遍歷,求後序遍歷
例:
前序遍歷: 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
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
using namespace std;
typedef struct node
{
char data;
struct node *leftchild;
struct node *rightchild;
} bitreenode,*bitree;
//*preorders是先序的字串,inorder是中序的字串
void posttraverse(char *preorder,char *inorder,int len)//求後序
{
if(len==0)return ;
int rootindex=0;
node newnode;
newnode.data=preorder[0];//先序字串的首元素是根節點
for(rootindex=0; preorder[0]!=inorder[rootindex]; rootindex++);//這一步是找到根節點在中序字串中的位置
posttraverse(preorder+1,inorder,rootindex);//遞迴遍歷左子樹
posttraverse(preorder+rootindex+1,inorder+rootindex+1,len-rootindex-1);//遞迴遍歷右子樹
cout<<*preorder;//列印根節點,為什麼要放在最後呢?因為這是求後序遍歷,如果是求先序遍歷
//就在遞迴之前列印根節點
}
void pretraverse(char *inorder,char *postorder,int len)//求先序
{
//類似知先序和中序求後序
if(len==0)return ;
int rootindex=0;
node newnode;
newnode.data=postorder[len-1];
cout<<postorder[len-1];
for(rootindex=0; postorder[len-1]!=inorder[rootindex]; rootindex++);
pretraverse(inorder,postorder,rootindex);
pretraverse(inorder+rootindex+1,postorder+rootindex,len-rootindex-1);
}
/*node* BinaryTreeFromOrderings(char* inorder, char* aftorder, int length)//求先序,同時建樹
{
if(length == 0)
{
return NULL;
}
node* Node = new node;//Noice that [new] should be written out.
Node->data= *(aftorder+length-1);
cout<<Node->data;
int rootIndex = 0;
for(;rootIndex < length; rootIndex++)//a variation of the loop
{
if(inorder[rootIndex] == *(aftorder+length-1))
break;
}
Node->leftchild = BinaryTreeFromOrderings(inorder, aftorder , rootIndex);
Node->rightchild= BinaryTreeFromOrderings(inorder + rootIndex + 1, aftorder + rootIndex , length - (rootIndex + 1));
return Node;
}*/
int main()
{
int i,j,k,cur,last;
char s1[1000],s2[1000];
while(scanf("%s",s1)!=EOF)
{
scanf("%s",s2);
//posttraverse(s1,s2,strlen(s1));
pretraverse(s1,s2,strlen(s1));
//BinaryTreeFromOrderings(s1,s2,strlen(s1));
cout<<endl;
}
/*
GDAFEMHZ
ADEFGHMZ
前一個是先序,後一個是中序
*/
//結果AEFDHZMG
/*
ADEFGHMZ
AEFDHZMG
前一個是中序,後一個是後序
*/
//結果GDAFEMHZ
return 0;
}
二、已知中序和後序遍歷,求前序遍歷
依然是上面的題,這次我們只給出中序和後序遍歷:
中序遍歷: 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
具體程式程式碼已在在上個程式碼中給出現在咱們具體來分析下以下語句:
for(rootindex=0; preorder[0]!=inorder[rootindex]; rootindex++);//這一步是找到根節點在中序字串中的位置
posttraverse(preorder+1,inorder,rootindex);//遞迴遍歷左子樹
posttraverse(preorder+rootindex+1,inorder+rootindex+1,len-rootindex-1);//遞迴遍歷右子樹
cout<<*preorder;
rootindex是根節點的位置,用它來表示左子樹和右子樹在字串中的長度
preorder+1是先序左子樹開始的位置,inorder的人是中序左子樹開始的位置,rootindex是左子樹長度
preorder+rootindex+1是右子樹開始的位置,同理inorder+rootindex+1是中序串右子樹開始的位置,len-rootindex-1是長度
要特別注意子樹開始的位置,不能弄錯了:如pretraverse(inorder,postorder,rootindex);
pretraverse(inorder+rootindex+1,postorder+rootindex,len-rootindex-1);後序字串中右子樹開始的位置是postorder+rootindex,不是postorder+index+1!!!!!!