二叉树的遍历 && [USACO3.4]美国血统 American Heritage && P1030 [NOIP2001 普及组] 求先序排列
简单描述
[USACO3.4]美国血统 American Heritage:
中序遍历+前序遍历-------------->后序遍历
P1030 [NOIP2001 普及组] 求先序排列:
中序遍历+后序遍历-------------->前序遍历
二叉树的遍历:
中序遍历+层次遍历-------------->先序序列
这三道题基本上包括了所有类似的问题
求同
这三道题都有共同点
也是关键点
通过中序遍历+其他遍历
来得到要求遍历
(如果是先序序列+后序序列,则不会得到唯一一棵树)
两种思路
- 建树后遍历
- 按照对应要求,建树时输出
存异
不同的地方也显而易见,除了给出的中序遍历相同外,给出的另一种遍历不同
前面思路讲解的比较模糊,这里详细的讲解一下
- 思路实现
以美国血统为例
样例:
中序:ABEDFCHG
前序:CBADEFGH
首先我们需要找出根(无论是那种思路,这是必需的
通过前序的定义(先序先访问的是根),第一个字母C一定是这个二叉树的根
那么中序会分为两部分,左子树{A B E D F},右子树{H G}
(因为中序是先遍历左子树,再访问根,再遍历右子树
由此可以得出根节点左边的是左子树,右边的是右子树
这个时候我们就知道左子树的元素个数为5,右子树元素个数为2
那再由此推出前序部分中左子树{B A D E F},右子树{G H};
这道题的要求为后序遍历,那就继续遍历左子树,直到叶节点
此时的树中序{A B E D F}
此时的树前序{B A D E F}
之前同理
中序部分,左子树{A},右子树{E D F}
前序部分,左子树{A},右子树{D E F}
再遍历左子树
此时的树,无论先序,中序皆为{A}
此时为叶节点,那就可以直接输出A
返回到这个节点的父节点
也就是
中序部分,左子树{A},右子树{E D F}
前序部分,左子树{A},右子树{D E F}
因为左子树遍历完了,在遍历右子树
此时的树
中序部分,右子树{E D F}
前序部分,右子树{D E F}
同理
中序部分,左子树{E},右子树{F}
前序部分,左子树{E},右子树{F}
剩下的请读者尝试模拟,毕竟自己的模拟出来的记得牢
这个时候,可能你已经发现这时递归实现,因为本身遍历就是递归实现
上述模拟的递归code
ft//先序
mt//中序
void lt(int a,int b,int c,int d){
if(a>b || c>d) return ;//需要中序和先序的条件同时满足
int p=mt.find(ft[a]);//寻根的place
//以根的位置把中序遍历分为两部分
lt(a+1,a+p-c,c,p-1);//a+1---a+p-c为先序左子树范围,c----p-1 为中序 //原因:p-c为左子树元素个数
lt(a+p-c+1,b,p+1,d);//a+p+1-c---b为先序右子树范围 ,p+1----q为中序 //原因:b-(p-c+1)为右子树元素个数
cout<<ft[a];
}
但是这个中序遍历+层次遍历的问题,略微有点不同
但上述三道题的根本:确定根,再由根去确定其他节点(当前根的左右子树的根
希望对您有所帮助
- 参考AC code
[USACO3.4]美国血统 American Heritage
建树后后续遍历
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
string ft,mt;
struct node{
char l;
char r;
}t[300];
void lt(int a,int b,int c,int d,char rt){//a----b为先序范围,c----d为中序范围
int p=mt.find(ft[a]);
if(a+1<=a+p-c && c<=p-1){
lt(a+1,a+p-c,c,p-1,ft[a+1]);//遍历左子树
t[rt].l=ft[a+1];
}
if(a+p-c+1<=b && p+1<=d){
lt(a+p-c+1,b,p+1,d,ft[a+p-c+1]); //遍历右子树
t[rt].r=ft[a+p-c+1];
}
}
void lt(char rt){
if(rt>='A' && rt <='Z'){
lt(t[rt].l);
lt(t[rt].r);
cout<<rt;
}
}
int main(){
cin>>mt>>ft;
int len=ft.length()-1;
lt(0,len,0,len,ft[0]);
lt(ft[0]);
return 0;
}
直接后续遍历
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
string ft,mt;
struct node{
char l;
char r;
}t;
void lt(int a,int b,int c,int d){
if(a>b || c>d) return ;//需要中序和先序的条件同时满足
int p=mt.find(ft[a]);//寻根的place
//以根的位置把中序遍历分为两部分
lt(a+1,a+p-c,c,p-1);//a+1---a+p-c为先序左子树范围,c----p-1 为中序 //原因:p-c为左子树元素个数
lt(a+p-c+1,b,p+1,d);//a+p+1-c---b为先序右子树范围 ,p+1----q为中序 //原因:b-(p-c+1)为右子树元素个数
cout<<ft[a];
}
int main(){
cin>>mt>>ft;
int len=ft.length()-1;
lt(0,len,0,len);
return 0;
}
二叉树遍历
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
char mt[1000],ct[1000];
void dfs(int a,int b,int c,int d){//a为中序区间左节点,b为右节点,x为层数
int i,j,t;
for(i=c;i<=d;++i){//在剩下的 A
bool book=0;
for(j=a;j<=b;++j)
if(ct[i]==mt[j]){
cout<<mt[j];
book=1;
t=i;
break;
}
if(book) break;
}
if(j>a) dfs(a,j-1,t,d);
if(j<b) dfs(j+1,b,t,d);
}
int main(){
int len;
memset(ct,'0',sizeof(ct));
memset(mt,'0',sizeof(mt));
cin>>mt;
len=strlen(mt);
for(int i=1;i<=len;++i){
mt[len-i+1]=mt[len-i];
cin>>ct[i];
}
dfs(1,len,1,len);
return 0;
}
P1030 [NOIP2001 普及组
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
string mt,lt;
void dfs(int a,int b,int c,int d){//a-b中序,c-d后序
if(c>d || a>b )return ;
int p=mt.find(lt[d]);
cout<<lt[d];
dfs(a,p-1,c,d-(b-p)-1);
dfs(p+1,b,d-(b-p),d-1);
}
int main(){
cin>>mt>>lt;
int len=mt.length()-1;
dfs(0,len,0,len);
return 0;
}
- 题目评测