1119 Pre- and Post-order Traversals (30 分)
不采用柳神的方式来做,柳神的太牛了,平常人看不懂。我就按照正常的建树规则来建树。
想要作出这种题,你需要自己先思考,把前序和后序画出来,自己建树构建一下,多加思考映像才能深刻。
思路:
很显然,前序的第一个值和后序的最后一个值是一样的。设置前序的左边为prel,右边为prer。后序的左边为postl,右边为postr。 那么可以发现前序中prel+1的下标值肯定是左子树的节点,后序的prer-1肯定是右子树的节点。通过这样我们可以确定左右节点,从而确定左右子树的区间。
但是如果前序的prel+1和后序的postr-1所对应的值相等,那么我们就不能区别是左子树还是右子树。所以这个地方就要另外考虑,当遇到这种值相等的情况我们可以设置设置一个flag 表示遇到多种情况的可能。然后默认选择左子树或者右子树来做即可。
可以采用如下的输入来试着自己构建树,并按照代码和上述思路来分析:
前序:1 2 3 4
后序:2 4 3 1
下面附上代码,配有详细的注释:
#include<iostream>
#include<vector>
using namespace std;
struct Node{
int val;
Node*left,*right;
Node(int v){
val=v;
left=right=NULL;
}
};
vector<int>preorder,postorder;
bool flag=false;
Node*create(int prel,int prer,int postl,int postr){
if(prel>prer||postl>postr) return NULL;
Node*root=new Node(preorder[prel]);
if(prel==prer||postl==postr) return root; //防止数组越界 因为相等的时候就只有一个了直接返回即可
int k1,k2; //这里k1是找右子树在前序中的位置 k2是指在后序中找左子树的位置。
for(k1=prel;k1<=prer;k1++){
if(preorder[k1]==postorder[postr-1]){ //postr-1 根据后序遍历的规则就是右子树的节点 ,,所以要在前序遍历中找到位置k1
break;
}
}
for(k2=postr;k2>=postl;k2--){
if(postorder[k2]==preorder[prel+1]) break; //prel+1根据前需遍历的规则就是左子树的节点 ,所以要再后序遍历找到位置k2
}
if(preorder[prel+1]!=postorder[postr-1]){
root->left=create(prel+1,k1-1,postl,k2);
root->right=create(k1,prer,k2+1,postr-1);
}else{
flag=true;//如果相等 说明不知道是左节点还是右节点。 这里默认当作左节点 ,即右子树不存在了
root->left=create(prel+1,prer,postl,k2); //这里第二个参数 不再是k1-1 因为已经找不到k1即右子树不存在
root->right=NULL;
}
return root;
}
int cnt=0;
void inorder(Node*root){
if(root==NULL) return;
inorder(root->left);
if(cnt==0) cout<<root->val;
else cout<<" "<<root->val;
++cnt;
inorder(root->right);
}
int main(){
int n,x;
cin>>n;
for(int i=0;i<n;i++){
cin>>x;
preorder.emplace_back(x); //1 2 3 4
}
for(int i=0;i<n;i++){
cin>>x;
postorder.emplace_back(x);//2 4 3 1
}
Node*root=create(0,n-1,0,n-1);
if(flag) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
inorder(root);
cout<<endl;
return 0;
}