后序遍歷與中序遍歷,求前序遍歷
首先,一點基本常識,給你一個后序遍歷,那么最后一個就是根(同理前序遍歷,第一個是根)
那么這個算法的核心就是不斷的求根;
接下來我用一個實例來說明怎樣進行求根:
例如以上,給出后序遍歷和中序遍歷,求前序遍歷
首先根據后序遍歷的最后一個就是根,可以知道4是根,以此可將前序和后序都分為三部分;
看上圖,在前序遍歷中,綠色框中的4為根,則在4的左邊,紅色框的就為以4為根的左子樹,黃色框的就是以4為根的右子樹。
則在找到4這個根之后,此題可以轉換為兩個子問題,一下:
求紅框的前序列,求黃框的前序列。
之后就可以遞歸了;我們再回到上面的圖,再進行一個小小的思考;
觀察前序遍歷和后序遍歷,都可以由三部分組成,紅框的左子樹,黃框的右子樹以及綠框的根;
那么,在進行函數遞歸的時候,重點就是怎樣計算各個部分的下標起始,來讓程序可以遞歸下去;
上代碼:
方法一:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; void beford(string in,string after){ if (in.size()>0){ char ch=after[after.size()-1]; cout<<ch;//找根輸出 int k=in.find(ch); beford(in.substr(0,k),after.substr(0,k)); beford(in.substr(k+1),after.substr(k,in.size()-k-1));//遞歸左右子樹; } } int main(){ string inord,aftord; cin>>inord;cin>>aftord;//讀入 beford(inord,aftord);cout<<endl; return 0; }
上面代碼的關鍵是,用substr函數將字符串進行切割,以此來達到分離字符串三部分的操作比較好理解。
方法二:
#include<iostream> using namespace std; int a[35],b[35]; void build(int l1,int r1,int l2,int r2) { if(l1>r1) return; cout<<" "<<b[r2]; int p = l1; while(a[p]!=b[r2]) { p++; } int cnt = p - l1; build(l1,p-1,l2,l2+cnt-1); build(p+1,r1,l2+cnt,r2-1); } int main() { int n; cin>>n; for(int i=0;i<n;i++) { cin>>b[i]; } for(int i=0;i<n;i++) { cin>>a[i]; } build(0,n-1,0,n-1); }
和上面的方法核心思想是不變的,但是有一點差別是,傳入函數的不再是序列整體,而是用左右下標的方式來進行遞歸;
特別注意,函數中的 cnt = p-l1, cnt是左子樹的長度,及黃框的長度,以此來進行遞歸;
已知前序和中序求后序的操作和以上一樣,只是將遞歸和查詢的位置變化,並且由於后序遍歷根是在最后才輸出,因此輸出的代碼應該放到搜索代碼的后面;
附上已知前序中序求后序的代碼:
#include<bits/stdc++.h> using namespace std; char a[500]; char b[500]; int n; void dfs(int l1,int r1,int l2,int r2) { if(l1>r1||l2>r2) return ; char x =a[l1]; int p=l2; while(b[p]!=x) { p++; } int ent = p-l2; dfs(l1+1,l1+ent,l2,p-1); dfs(l1+ent+1,r1,p+1,r2); cout<<x;// 特別注意輸出代碼的位置! } int main() { scanf("%d",&n); scanf("%s",&a); scanf("%s",&b); dfs(0,n-1,0,n-1); }
以上。可能講的有點恍惚,有不懂的請評論留言;