对一棵二叉树,如果给出前序遍历和中许遍历的结点访问顺序,那么后序遍历的顺序是唯一确定的,也很方便地求出来。但如果现在只知道前序遍历和后序遍历的顺序,中序遍历的顺序是不确定的,例如:前序遍历的顺序是ABCD,而后序遍历的顺序是CBDA,那么就有两课二叉树满足这样的顺序(见图(1)和图(2))。
给定前序遍历和后序遍历的顺序,求出总共有多少棵不同形态的二叉树满足这样的遍历顺序。
输入格式:
整个输入有两行,第一行给出前序遍历的访问顺序,第二行给出后序遍历的访问顺序。
二叉树的结点用一个大写字母表示,不会有两个结点标上相同字母。输入数据不包含空格,且保证至少有一棵二叉树符合要求。
输出格式:
输出一个整数,为符合要求的不同形态二叉树的数目。
样例输入:
ABCD
CBDA
样例输出:
2
样例输入:
QWERTYUIOPAS
ROIUYASPTEWQ
样例输出:
32
思路:只需要明确何时中序遍历会出现两种情况即可。
如上图所示,当出现某一节点只有一个子节点时,就会出现两种中序遍历的情况。
前序遍历:ABC (子树1前序遍历) (子树2前序遍历) D (子树3前序遍历) (子树4前序遍历)
后序遍历:(子树1后序遍历) (子树2后序遍历) CB (子树3后序遍历) (子树4后序遍历) DA
中序遍历:
上图树1:(子树1中序遍历) C (子树2中序遍历) BA (子树3中序遍历) D (子树4中序遍历)
上图树2:B (子树1中序遍历) C (子树2中序遍历) A (子树3中序遍历) D (子树4中序遍历)
那就找这种节点就好了,找到n个节点的话就有 2^n 种不同的树满足给定的前序遍历和后序遍历。
方法1:
从根节点开始,依次查找该节点的左子树和右子树有没有所要求的节点。
依旧按照上图所示的例子,就可以发现左子树的根节点就在前序遍历的第二个位置,右子树的根节点就在后序遍历的倒数第二个位置。
1.检查当前根节点是否只有一个子树,满足的话就让 cont 加1。如果当前序列只有两个元素很明显它满足只有一个子树的条件,直接 cont 加1返回。
2.我们需要做的就是正向遍历前序遍历序列到右根节点时停止,之前的(除当前根节点)就是左子树的前序遍历序列,而剩余的就是右子树的前序遍历序列。
3.然后反向遍历后序遍历序列到左根节点后停止,之后的就是右子树的后序遍历序列,之前的(除当前根节点)就是左子树的后序遍历序列。
4.找到左右子树的遍历序列后,递归调用该函数继续查找。
代码如下:
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int cont=0;
void fun(string pre,string post){
if(pre.length()<2&&post.length()<2) return;//递归结束
if(pre.length()==2&&post.length()==2){ //满足只有一颗子树的条件,cont加1
cont++;
return;
}
char leftnode=pre[1],rightnode=post[post.length()-2]; //找到左右根节点
if(leftnode==rightnode){ //只有左子树or只有右子树
cont++;
string nextpre=pre.substr(1,pre.length()-1),nextpost=post.substr(0,post.length()-1);
fun(nextpre,nextpost);
return;
}
string leftpre,leftpost,rightpre,rightpost;
int i=0;
for(i=1;i<pre.length();i++){ //找到左子树前序遍历序列
if(pre[i]==rightnode){
break;
}
leftpre+=pre[i];
}
rightpre=pre.substr(i,pre.length()-i);//找到右子树前序遍历序列
for(i=post.length()-2;i>=0;i--){ 找到右子树后序遍历序列
if(post[i]==leftnode){
i++;
break;
}
}
rightpost=post.substr(i,post.length()-1-i);
leftpost=post.substr(0,i);// 找到左子树后序遍历序列
fun(leftpre,leftpost);
fun(rightpre,rightpost);
}
int main(){
string pre,post;
cin >> pre >> post;
fun(pre,post);
cout << (int)pow(2,cont) << endl;
}
方法2:(比方法1好多了呢)
同样时找只有一个子节点的节点。
观察前序遍历和后续遍历可以得知,如果存在这样的节点,那么该节点和它仅有的子节点在前序遍历和后序遍历的序列中一定反向连续存在。
具体原因就不多说了,仔细想一想还是很简单的。
那么我们只需要做查找子串就好了。
代码:
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int cont=0;
int main(){
string pre,post;
cin >> pre >> post;
for(int i=0;i<pre.length()-1;i++){
string temp;
temp+=pre[i+1];
temp+=pre[i];
if(post.find(temp)!=-1) cont++;
}
cout << (int)pow(2,cont) << endl;
}