二叉樹的先序,中序,后序如何遍歷,不在此多說了。直接看題目描述吧(題目摘自九度oj劍指offer面試題6):
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並輸出它的后序遍歷序列。
- 輸入:
輸入可能包含多個測試樣例,對於每個測試案例,
輸入的第一行為一個整數n(1<=n<=1000):代表二叉樹的節點個數。
輸入的第二行包括n個整數(其中每個元素a的范圍為(1<=a<=1000)):代表二叉樹的前序遍歷序列。
輸入的第三行包括n個整數(其中每個元素a的范圍為(1<=a<=1000)):代表二叉樹的中序遍歷序列。
輸出
對應每個測試案例,輸出一行:
如果題目中所給的前序和中序遍歷序列能構成一棵二叉樹,則輸出n個整數,代表二叉樹的后序遍歷序列,每個元素后面都有空格。
如果題目中所給的前序和中序遍歷序列不能構成一棵二叉樹,則輸出”No”。
下面看一下解題思路:
二叉樹的問題,我們首先想到遞歸思想,即大問題的解題思路和小問題的解決思路是一樣的。先來分析二叉樹的先序和中序遍歷序列能給我們哪些有用的信息,首先先序遍歷可以告訴我們二叉樹的根節點(即先序遍歷序列的第一個元素)。其次,我們可以通過查詢得到二叉樹的根節點在中序遍歷序列中的位置(假設為L),那么中序遍歷序列L之前的是左子樹,之后的右子樹。知道這兩部分信息之后,我們在考慮后序遍歷,對於一個二叉樹來說,根節點是后序遍歷的最后一個去遍歷的節點。
經過以上分析,我們就可以這樣解題了(如下圖):
現在要考慮的是,先遞歸右子樹還是左子樹,因為后序遍歷是先左后右,而我們現在是從后往前得到后序遍歷序列,所以先遞歸右子樹。還有一個問題,即什么時候不能構成二叉樹,本人以為只要根節點在中序遍歷序列中查找不到就不能構成二叉樹。
題目就分析到這,下面給出C語言完整代碼,在九度oj上已AC,代碼如下:
#include<stdio.h> #include<stdlib.h>
/* *已知二叉樹的先序和中序遍歷,求后序遍歷 */
int pos[1000]; int n; int flag=0; int locationInMid(int mid[],int ms,int me,int h) { int i; for(i=ms;i<=me;++i) { if(mid[i]==h) return i; } return -1; } void postOrder(int pre[],int ps,int pe,int mid[],int ms,int me) //有待改進的地方,函數的參數可以簡化,因為數組的地址是0號元素的地址,知道頭結點在mid中的位置后,左右子樹的個數即可得到
{ int hm=0,cl=0,cr=0; pos[--n]=pre[ps]; //--n;
hm=locationInMid(mid,ms,me,pre[ps]); if(hm==-1) { // printf("No\n");
flag=-1; return ; } else { cl=hm-ms; cr=me-hm; if(cr>0) postOrder(pre,ps+cl+1,pe,mid,hm+1,me); if(cl>0) postOrder(pre,ps+1,ps+cl,mid,ms,hm-1); } return ; } int main() { int pre[1000]; int mid[1000]; int i,t; while(scanf("%d",&n)!=EOF) { t=n; for(i=0;i<n;++i) scanf("%d",&pre[i]); for(i=0;i<n;++i) scanf("%d",&mid[i]); postOrder(pre,0,n-1,mid,0,n-1); if(flag==-1) { printf("No\n"); flag=0; } else { for(i=0;i<t;++i)//注意:n是全局變量所以上一個語句執行完之后,n的值已經不是原來的n了,所以要用t保存n最初的值
printf("%d ",pos[i]); printf("\n"); } } return 0; }
本題的代碼並不是最簡潔的,但最容易理解,其實ps,pe,ms,me四個變量並不完全需要,因為pre指向的pre[ps],而知道二叉樹的節點個數和根節點在中序遍歷中的位置即可求得左右子樹的位置,大家可以思考一下,優化一下代碼。