二叉樹的遍歷 &【NOIP2001普及組】& 洛谷 P1030 求先序排列
阿新 • • 發佈:2019-05-01
題目 輸出 image pre ext bubuko 二叉樹 找到 pan
題目鏈接
https://www.luogu.org/problemnew/show/P1030
模板題
先講一下二叉樹的遍歷
二叉樹的遍歷
- 分類
- 性質
- 求法
分為三類:
- 先序遍歷(PreOrder):根節點→左子樹→右子樹
- 中序遍歷(InOrder):左子樹→根節點→右子樹
- 後序遍歷(PostOrder):左子樹→右子樹→根節點
我們可知:
- **序遍歷實際上是指根節點的位置
- 無論哪種遍歷順序,左子樹都在右子樹的前面
- 在前序遍歷中,第一個點是根節點
- 在後序遍歷中,最後一個點是根節點
例如這樣一個二叉樹:
它的先序遍歷:A--B--D--E--X--C--F--Y--Z
它的中序遍歷:D--B--X--E--A--Y--F--Z--C
它的後序遍歷:D--X--E--B--Y--Z--F--C--A
求後序遍歷
用到遞歸的思想,求整個二叉樹的後序遍歷就是求每個子樹的後序遍歷,最後連接起來即可。
1 #include<iostream> 2 using namespace std; 3 string z,q; 4 int len,cnt; 5 void PostOrder(int l,int r){//求中序遍歷中l到r這個子樹的後序遍歷 6 if(l>r) return; //邊界條件 7 int i; 8 char ans=q[cnt++]; //先序遍歷的第一個是根節點 9 for(i=l;i<=r;i++){ 10 if(z[i]==ans) break;//找到根節點在中序遍歷中的位置 11 } 12 PostOrder(l,i-1); //遞歸左子樹 13 PostOrder(i+1,r); //遞歸右子樹 14 cout<<ans; //註意後序遍歷是左右根的順序,所以最後輸出根 15 } 16 int main() 17 { 18 cin>>z>>q; //z是中序遍歷,q是先序遍歷 19 len=z.length()-1; 20 PostOrder(0,z.length()-1);//一開始是整個子樹 21 return 0; 22 }
求先序遍歷
這比求後序遍歷稍微有些復雜,需要保留根節點,即:PreOrder(左端點,右端點,根節點)。這是因為根節點在先序遍歷中是從前往後排列的,而在後序遍歷中不是這樣的。
還是這個圖:
它的先序遍歷:A--B--D--E--X--C--F--Y--Z
它的中序遍歷:D--B--X--E--A--Y--F--Z--C
它的後序遍歷:D--X--E--B--Y--Z--F--C--A
在先序遍歷中,根節點依次是A,B,D,E,X……是按照從前往後的順序排列的,所以可以直接 ans=q[cnt++]; 而在後序遍歷中卻不是這樣。
有人或許會說:那後序遍歷中根節點是從後往前排列的,其實這是一個錯誤的結論。還是看這一個二叉樹,後序遍歷是A,C,F,Z,Y,B……顯然是按照根→右子樹→左子樹排列的,而我們求的先序排列是根→左→右,顯然這種方法不行。
所以我們需要多傳一個參數,來記錄根節點在後序遍歷中的位置。重點:確定子樹的根節點在後序遍歷中的位置。
1 #include<iostream> 2 using namespace std; 3 string z,h; 4 int len; 5 void PreOrder(int l,int r,int root){//求中序遍歷中l到r這個子樹(以root為根)的後序遍歷 6 if(l>r) return; 7 int i; 8 for(i=l;i<=r;i++){ //和求後序遍歷一樣 9 if(z[i]==h[root]) break; 10 } 11 cout<<h[root]; //註意是根左右 12 PreOrder(l,i-1,root-(r-i)-1);//左子樹的根節點就是原來根節點減去右子樹的節點數的上一個(r-i就是右子樹的節點數) 13 PreOrder(i+1,r,root-1); //右子樹的根節點就是後序遍歷中原來根節點的上一個 14 } 15 int main() 16 { 17 cin>>z>>h; 18 len=h.length()-1; 19 PreOrder(0,z.length()-1,len); 20 return 0; 21 }
而在知道先序遍歷和後序遍歷的情況下,中序遍歷是不唯一的,但可以求出情況數(後面將做補充)。
//NOIP2001普及組t3
二叉樹的遍歷 &【NOIP2001普及組】& 洛谷 P1030 求先序排列