二叉树的遍历 && [USACO3.4]美国血统 American Heritage && P1030 [NOIP2001 普及组] 求先序排列

简单描述
[USACO3.4]美国血统 American Heritage:
中序遍历+前序遍历-------------->后序遍历
P1030 [NOIP2001 普及组] 求先序排列:
中序遍历+后序遍历-------------->前序遍历
二叉树的遍历:
中序遍历+层次遍历-------------->先序序列

这三道题基本上包括了所有类似的问题

求同
这三道题都有共同点
也是关键点
通过中序遍历+其他遍历
来得到要求遍历

(如果是先序序列+后序序列,则不会得到唯一一棵树)

两种思路

  1. 建树后遍历
  2. 按照对应要求,建树时输出

存异
不同的地方也显而易见,除了给出的中序遍历相同外,给出的另一种遍历不同

前面思路讲解的比较模糊,这里详细的讲解一下

  • 思路实现

以美国血统为例
样例:
中序: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;
}
  • 题目评测

P1827 [USACO3.4]美国血统 American Heritage

1364:二叉树遍历(flist)

P1030 [NOIP2001 普及组] 求先序排列

posted @ 2021-02-18 20:45  归游  阅读(41)  评论(0编辑  收藏  举报