洛谷 P1030:求先序排列

洛谷 P1030:求先序排列

题目地址

【题目描述】
  给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度≤8)。
【输入格式】
  2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
【输出格式】
  1行,表示一棵二叉树的先序。
【输入样例】
  BADC
  BDCA
【输出样例】

  ABCD

(一)什么是先序、中序、后序遍历:
  先序遍历:先遍历当前子树根结点,再递归遍历左子结点,最后递归遍历右子结点
  中序遍历:先递归遍历左子结点,再遍历当前子树根结点,最后递归遍历右子结点
  后序遍历:先递归遍历左子结点,再递归遍历右子结点,最后遍历当前子树根结点
  即先序、中序、后面的遍历都是针对根结点而言的。如还是不明白,自行百度,这里不做详解
  
(二)已知先序遍历和中序遍历如何求后序遍历:
  已知一颗二叉树:
  先序遍历为:ABCDEFG
  中序遍历为:CBEDAFG
  求该二叉树的后序遍历。
 1.首先我们要知道,对于先序遍历,第一个遍历到的结点一定是整颗树的根结点,后面依次是左子树和右子树,也就是说先序遍历的结构大概是(根)(左子树)(右子树);对于中序遍历,整棵子树的根在序列中间,根前面是左子树,根后面是右子树,所以中序遍历的结构大概是(左子树)(根)(右子树)
 
 2.所以很容易看出A是整棵子树的根,CBED是左子树,FG是右子树(如果还不明白多读读1)
 (A)(BCDE)(FG)         (CBED)(A)(FG)
在这里插入图片描述
 3.对于剩下的子树,我们要如何确定子树的根和左右子树呢?其实方法和前面一模一样,比如对于先序遍历中的子树(BCDE),根据1很容易判断B是这棵子树的根(子树的第一个字母),然后我们再去中序遍历中找到B的位置,B左边的就是左子树,右边的就是右子树。即在以B为根的子树中,C是左子树,ED是右子树。
在这里插入图片描述
 4.根据同样的方法,我们可以得到,对于子树(DE),在先序遍历中为(DE),所以D为根,在中序遍历中为(ED),所以E是D的左子树;在子树(FG)中,在先序遍历中为(FG),所以F是根,在中序遍历中为(FG),所以G是F的右子树。这样我们就确定了一个形态唯一的一棵二叉树了
在这里插入图片描述
 总的来说:我们只需要每次去先序遍历中找到根(子树的第一个结点),再根据这个根去中序遍历中找到当前根的左右子树就可以了。至于为什么不按照题目中给定的遍历方法讲解,明显是留给你自己思考啰,原理都是一样的,如果你自己推导出来了说明你已经学会了,否则,只能多看几遍了呗。

(三)已知其中两种遍历的结果一定可以确定最后一种遍历结果吗?
  根据前面我们的推导,我们发现每次不停的去先序遍历中找根,去中序遍历中找左右子树,最后一定可以确定一棵唯一的一棵二叉树,即当前子树中只有一个结点的时候,子树的形态也就唯一确定了。当子树形态确定后,他的各种遍历方式的结果肯定也就唯一了。那么是否存在不唯一确定的情况呢?答案是存在的。如果我们只知道先序和后序遍历,确定的二叉树就不唯一
 我们知道,先序确定根,后序也是确定根(先序根在第一个位置,后序根在最后一个位置),如果只知道先序和后序,我们是没有办法确定中间部分哪些是左子树哪些是右子树的,所以也就无法确定树的形态。
 比如:
 先序abc
 后序cba
 很明显,只要是abc依次在一条链上的情况他都是满足要求的,但是左右子树顺序完全没办法确定.
在这里插入图片描述
  虽然我们不能确定唯一的树的形态,但是我们可以算出所有满足条件的二叉树的形态有多少种啊,
  比如洛谷1229:遍历问题  题目地址
  
  前面说了这么多都是实际模拟,但是最重要的代码实现还没说的,下面直接上代码(带注释)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s1[20],s2[20];
int k; 
void work(int l1,int r1,int l2,int r2)
//[l1,r1]表示中序遍历的子树区间
//[l2,r2]表示后序遍历的子树区间 
{
	if(l1>r1)//递归终止条件 
	return ;
	printf("%c",s2[r2]);//找根并输出,先序遍历本来就是先输出根结点 
	for(int i=l1;i<=r1;i++)
	{
		if(s1[i]==s2[r2])//在s1中找到根,分成左右子树 
		{
			k=i;
			break;
		}
	}
	work(l1,k-1,l2,l2+k-l1-1);//继续遍历左子树 
	work(k+1,r1,l2+k-l1,r2-1);//继续遍历右子树,注意要去掉当前的根 
	//注意无论哪种遍历方式,子树长度肯定是一样的 
	return ; 
}
int main()
{
	scanf("%s%s",s1,s2);
	int l1=strlen(s1);
	int l2=strlen(s2);//记录两个字符串的长度 
	work(0,l1-1,0,l2-1);
	//既然每次操作步骤一样,肯定用递归啊
	return 0; 
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值