中序+前序 ——> 后序
法一:将数组传参
由中序遍历和前序遍历得到后续遍历(镜像翻转或不翻转)
这是之前的老做法,把数组作为参数,每次传具体某一段数组的首地址
麻烦的地方在于,有时候找不准某段数组的首地址
以下标0开始的数组,str【i】为首的数组段,地址是str+i
以下标1开始的数组,str【i】为首的数组段,地址也是str+i,不要搞错了,从下标1开始存储,str【i】的地址也是不变的
#include <bits/stdc++.h>
using namespace std;
const int N=35;
vector<int> v;
void print(int in[],int pre[],int len){
if(len==0)return ;
int ch=pre[0];
int i;
for(i=0;i<len;i++){//0 1 2 3 4
if(in[i]==ch)break;
}//in[0]~in[i-1]左,in[i]中,in[i+1]-in[len-1]右
// print(in,pre+1,i);//左
print(in+i+1,pre+i+1,len-i-1);//右 //不进行镜像翻转是2315764
print(in,pre+1,i);//左 镜像翻转是7 5 6 2 3 1 4
cout<<ch<<" ";
}
signed main(){//45
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
int in[N];
int pre[N];
for(int i=0;i<n;i++){
cin>>in[i];
}
for(int i=0;i<n;i++){
cin>>pre[i];
}
print(in,pre,n);
return 0;
}
法二:将中序下标传参,前序的root传参(前序遍历中总能找到 根节点、左子树根节点、右子树根节点)
异曲同工,可以将传地址改为传递下标,每次只需要根据下标确定左、右子树所在的数组段,通过比较下标边界也可以作为递归出口,原来是根据数字段长度
#include <bits/stdc++.h>
using namespace std;
const int N=35;
int in[N];//左中右
int pre[N];//中左右
void print(int l,int r,int root){
if(l>r)return ;
int ch=pre[root];
int i;
for(i=l;i<=r;i++){
if(in[i]==ch)break;
}//in 左l~i-1 中i 右 i+1 ~r ,
// 从而得到左长度 i-l 右长度r-i,从而得到in数组左右段
print(l,i-1,root+1);//左
print(i+1,r,root+i-l+1);//右 左+中长度是i-l+1,pre右子树第一个节点是root+i-l+1-1 +1,也就是右子树的根节点
cout<<ch<<" ";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>in[i];
}
for(int i=0;i<n;i++){
cin>>pre[i];
}
print(0,n-1,0);//in数组的下标范围,从而确定左右子树所在的数组段
//注意,要加上root参数,方能发挥pre数组寻找根节点的作用
//曾记否,当时把数组当作参数,是pre数组和in数组的下标都要重新确定的
//所以print(0,n-1),只传in数组的下标变化是不够的
return 0;
}
中序+前序 ——> 层次遍历
这里的level数组可以用map容器代替,否则节点数一多,序号的上限非常大,是2的N次幂,还要初始化为-1
不如用map容器,没有的就相当于上面初始化为-1的
#include <bits/stdc++.h>
using namespace std;
const int N=35;
int in[N];//左中右
int pre[N];//中左右
int level[300000];
void print(int l,int r,int root,int index){
if(l>r)return ;
int ch=pre[root];
int i;
for(i=l;i<=r;i++){
if(in[i]==ch)break;
}//in 左l~i-1 中i 右 i+1 ~r ,
// 从而得到左长度 i-l 右长度r-i,从而得到in数组左右段
level[index]=ch;
print(i+1,r,root+i-l+1,index*2+1);
//右 左+中长度是i-l+1,pre右子树第一个节点是root+i-l+1-1 +1,也就是右子树的根节点
print(l,i-1,root+1,index*2+2);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>in[i];
}
for(int i=0;i<n;i++){
cin>>pre[i];
}
int index=0;
fill(level,level+100000,-1);
print(0,n-1,0,index);//中序下标,前序根节点、层次遍历的序号
int cnt=0;
for(int i=0;;i++){
if(level[i]!=-1){
cnt++;
cout<<level[i];
if(cnt==n)break;
cout<<" ";
}
}
return 0;
}
map容器(因为层次遍历下标可能很大
#include <bits/stdc++.h>
using namespace std;
const int N=35;
int in[N];//左中右
int pre[N];//中左右
//int level[300000];
map<int,int> level;
void print(int l,int r,int root,int index){
if(l>r)return ;
int ch=pre[root];
int i;
for(i=l;i<=r;i++){
if(in[i]==ch)break;
}//in 左l~i-1 中i 右 i+1 ~r ,
// 从而得到左长度 i-l 右长度r-i,从而得到in数组左右段
level[index]=ch;
print(i+1,r,root+i-l+1,index*2+1);
//右 左+中长度是i-l+1,pre右子树第一个节点是root+i-l+1-1 +1,也就是右子树的根节点
print(l,i-1,root+1,index*2+2);
// 由于输出顺序是由下标决定的,先print左子树或是右子树结果一样
// 要镜像翻转,就得把左子树在层次遍历中的序号和右子树交换
// 左子树index*2+2 , 右子树index*2+1,不是+1和+0,是因为下标从0开始
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>in[i];
}
for(int i=0;i<n;i++){
cin>>pre[i];
}
int index=0;
print(0,n-1,0,index);//中序下标,前序根节点、层次遍历的序号
// int cnt=0;
// for(int i=0;i<level.size();i++){
// cout<<level[i];
// if(i!=n-1)cout<<" ";
// }
map<int,int>::iterator it=level.begin();
cout<<it->second;
it++;
for(;it!=level.end();it++)cout<<" "<<it->second;
return 0;
}