復原二叉樹
http://codeup.cn/problem.php?cid=100000611&pid=0
題目簡述:
給你一棵二叉樹的前序遍歷和中序遍歷,要求輸出這棵二叉樹的后序遍歷
輸入無限組,沒有停止要求(文件讀入)
補充知識:
既然做到了這道題,那就補充一下關於還原二叉樹及二叉樹遍歷的知識,如下:
- 二叉樹遍歷
1.前序遍歷:按照“根、左、右”的順序遍歷一棵樹
2.中序遍歷:按照“左、根、右”的順序遍歷一棵樹
3.后序遍歷:按照“左、右、根”的順序遍歷一棵樹
4.層序遍歷:按層從根節點開始由上至下,每一層由左至右的順序遍歷一棵樹
(層序遍歷相當於對二叉樹從根節點開始的BFS廣度優先搜索)
- 根據遍歷還原二叉樹
1.知道前序和中序,樹是唯一的。具體還原步驟如下:
(1)前序遍歷的第一位是這棵樹的Root(根節點)
(2)在中序遍歷中找到Root的位置為index,在Root左邊的即為Root的左子樹,右邊的則是右子樹(特別的,如果沒有元素在左邊或右邊,則說明該節點沒有左子樹或右子樹)
(3)再在前序遍歷中找到Root的下一位,即為Root左子樹的根節點root(注意一下區別)
(4)然后像Root一樣在中序中找到root的位置,在root左邊的即為root的左子樹,右邊的則是右子樹
(5)不斷重復以上操作(其實重復1、2即可),一棵樹就復原出來了
2.知道中序和后序,樹是唯一的。具體步驟如下:
(1)因為后序是最后訪問根,所以后序的最后一個節點是這棵樹的Root
(后序處理其實可以看做將前序倒過來了。注意:只是操作像倒過來,並不是說前序反轉就是后序!)
(2)在中序中找到Root的位置,以此划分左右子樹
(3)然后重復1、2步驟,迭代遞歸求解即可復原樹
PS:例題洛谷P1030:求先序排列
3.知道前序和后序,樹不是唯一的。原因如下:
前序和后序在本質上都是將父節點與子節點進行分離,但並沒有指明左子樹和右子樹的能力,因此得到這兩個序列只能明確父子節點關系,而不能確定一棵二叉樹。
來一波文字+圖幫助大家理解qvq(以題目第二個輸入為例)
4.知道中序和層序,樹是唯一的。具體步驟如下:
(1)在層序遍歷中循環中序遍歷,每一次循環,找到一個根節點就輸出,然后記錄其在中序遍歷中的位置t后直接break
(2)然后根據t來划分左右子樹,然后再進入遞歸循環處理子樹
PS:大家可以在草稿本上手模一遍,更好理解quq(雖本人在做題的時候對着草稿本想了1h,然而並沒有什么用)
下面會給出根據中序+層序復原二叉樹並輸出前序遍歷的例題和代碼
總結一下:
知道前序中序或后序中序或層序中序,可以復原唯一的樹;而只知道前序和后序和層序之中的兩個或三個只能明確父子節點關系。
原理:前序或后序可以確立每一個子樹的root,而中序則可以划分左右子樹,然后迭代就可以復原一棵樹
提示:
知道了原理,不妨自己先試着編一下代碼,這樣有助於理解下面給出的代碼,也更能找出自己的錯誤qwq
代碼Code:
因為這是模板題,所以思路就是如上補充的知識(如果不是很懂,可以根據下面代碼進行理解,自認為很詳細)
代碼如下:
#include <bits/stdc++.h>
using namespace std;
char pre[100001];
char in[100001];
struct node {
char data; //數據域
node *lc; //指向左子樹根節點的指針
node *rc; //指向右子樹根節點的指針
};
inline void build(node * &t,int prel,int prer,int inl,int inr) {
if(prel>prer) { //前序序列長度小於等於0時,直接返回
t=NULL;
return ;
}
t=new node(); //申請一個node型變量的地址空間,用來存放當前二叉樹的根節點
t->data=pre[prel]; //節點權值為pre[prel]
int index;
for(index=inl;index<=inr;index++) {
if(in[index]==pre[prel]) break; //在中序序列中找到當前的根節點位置,存在index中
}
int numl=index-inl; //左子樹的節點個數
build(t->lc,prel+1,prel+numl,inl,index-1); //分別遞歸左右子樹
build(t->rc,prel+numl+1,prer,index+1,inr);
}
inline void postorder(const node *t) {
if(t==NULL) return ; //到達空樹,遞歸邊界
postorder(t->lc); //訪問左子樹
postorder(t->rc); //訪問右子樹
printf("%c",t->data);
}
int main() {
while(cin>>pre>>in) { //連續輸入
node *root=NULL; //新建空根節點root
build(root,0,strlen(pre)-1,0,strlen(pre)-1); //開始復原二叉樹
postorder(root); //根據復原的二叉樹輸出后序遍歷
puts("");
memset(pre,'0',sizeof(pre)); //清空好習慣qwq
memset(in,'0',sizeof(in));
}
return 0;
}
中序+層序-->復原二叉樹+前序
#include <bits/stdc++.h>
using namespace std;
int len;
char in[10001],layer[10001];
inline void preorder(int left,int right) {
int t;
bool flag=false;
for(register int i=0;i<len;i++) { //層序
for(register int j=left;j<=right;j++) { //中序
if(layer[i]==in[j]) {
cout<<in[j]; //找到一個就輸出一個
//cout<<" "<<left<<","<<right<<" "<<j<<endl;
t=j;
flag=1;
break;
}
}
if(flag==1) break;
}
if(left<t) preorder(left,t-1); //划分左右子樹
if(t<right) preorder(t+1,right);
}
int main() {
cin>>in>>layer;
len=strlen(in);
preorder(0,len-1);
return 0;
}