题目的意思就是给出二叉树的前序遍历和后序遍历,求中序遍历有几种可能。
原题点这里-->P1229 遍历问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:
所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。
输入格式
共两行,第一行表示该二叉树的前序遍历结果 s1,第二行表示该二叉树的后序遍历结果 s2。
输出格式
输出可能的中序遍历序列的总数,结果不超过 。
输入输出样例
输入 #1
abc cba
输出 #1
4
解题过程
解题前我们需要知道以下几个点
1.给出前序遍历和后序遍历不能确定中序遍历的原因是,当某节点只有一个孩子时,我们不能通过前序遍历和后序遍历确定这个孩子是左孩子还是右孩子,如样例所述。
2.如果没有如1所描述的节点时中序遍历有且仅有一种可能,每多一个这样的节点中序遍历的可能数量翻倍。即,当二叉树只有一个孩子并且不知道该孩子是左孩子还是右孩子的节点有n个时,中序遍历的可能数为。由此我们就能把问题转化为找只有一个孩子的节点。
3.前序遍历的第一个节点一定是根节点,第二个节点一定是根结点的子节点。后序遍历的最后一个节点一定是根节点,倒数第二个节点一定是根节点的子节点。
4.当根节点有两个子节点时.前序遍历的第二个节点(根节点的左孩子)和后序遍历的倒数第二个节点(根节点的右孩子)一定不同;
当根节点只有一个子节点时前序遍历的第二个节点和后序遍历的倒数第二个节点一定相同,且不确定该子节点是左孩子还是右孩子。
总体思路
知道以上几个点后这道题就变得简单了
得到前序遍历和后序遍历,首先.前序遍历的第一个节点和后序遍历的最后一个节点一定是根节点,然后我们对比前序遍历的第二个节点和后序遍历的倒数第二个节点,如果一样,则根节点只有一个子节点,中序遍历可能总数乘2,去掉根节点后得到新树的前序遍历和后序遍历;如果不一样,则根节点有两个子节点,找到两个子节点,去掉根节点后得到左右两个子树的前序遍历和后序遍历。
继续这个过程直到遍历完所有节点,我们就得到了正确答案。
源代码
#include<stdio.h>
#include<string.h>
char a[10000],b[10000];//储存原树的前序遍历和后续遍历
unsigned long long result = 1;//用ull防止存不下
void work(int qa,int za,int qb,int zb)//前序遍历首位、末位的下标,后序遍历首位、末位的下标
{
if(za-qa<=0)
{
return;
}
if(a[qa+1]==b[zb-1])//根节点只有一个子节点
{
result*=2;
work(qa+1,za,qb,zb-1);//去掉根节点后得到新的前序遍历和后序遍历
}
else//根节点有两个子节点
{
for(int i=2;i<=za-qa;i++)//找到左孩子在后序遍历中的位置
{
if(a[qa+1]==b[zb-i])
{
work(qa+1,za-i+1,qb,zb-i);//左子树的前序遍历和后序遍历
work(za-i+2,za,zb-i+1,zb-1);//右子树的前序遍历和后序遍历
return;
}
}
}
return;
}
int main()
{
scanf("%s",a);
scanf("%s",b);
work(0,strlen(a)-1,0,strlen(b)-1);
printf("%llu",result);
return 0;
}