树和二叉树基本上都有先序、中序、后序、按层遍历等遍历顺序,给定中序和其它一种遍历的序列就可以确定一棵二叉树的结构。
假定一棵二叉树一个结点用一个字符描述,现在给出中序和按层遍历的字符串,求该树的先序遍历字符串。
输入格式
两行,每行是由大写字母组成的字符串(一行的每个字符都是唯一的),分别表示二叉树的中序遍历和按层遍历的序列。
输出格式
一行,表示二叉树的先序序列。
数据范围
输入字符串的长度均不超过26。
输入样例:
DBEAC
ABCDE
输出样例:
ABDEC
代码
#include <iostream>
#include<map>
using namespace std;
struct Node{
char val;
Node * left,*right;
Node(char val):val(val),left(NULL),right(NULL){}
};
string inorder,lorder;//inorder中序遍历、lorder按层遍历
bool visit[30];
//前、中、后三种遍历都是通过dfs遍历的,只需调整cout<<root->val;的位置即可输出其中的一种遍历
void dfs(Node * root){
if(!root)
return;
cout<<root->val;//输出根结点
dfs(root->left);//遍历左子树
dfs(root->right);//遍历右子树
}
int main()
{
//给定中序和先序、后序、按层遍历中的一种遍历的序列就可以确定一棵二叉树的结构。
//因为中序遍历可以确定根左右子树的结点个数,先序、后序、按层遍历能确定根的位置
//知道这两个信息就可以唯一的确定一颗二叉树了
cin>>inorder>>lorder;//输入中序、按层遍历的序列
//因为在中序遍历中查找某个值的下标需要O(n)的复杂度,可以使用hash降为O(1)
map<char,int> m;
Node* q[30];//还原按层遍历的队列,q[i]代表层序遍历中的第几个输出元素(null不算,下标从0开始)
for(int i=0;i<inorder.size();i++)
m[inorder[i]]=i;//将中序遍历的值和下标映射到map中
q[0]=new Node(lorder[0]);//将整个树的根放在q[0]的位置
for(int i=0,j=1;j<lorder.size();){//按层遍历,i代表当前层的起点,j是下一层的起点
//内层循环遍历当前层
for(int e=j;i<e;i++){
int root=m[lorder[i]];//查找根在中序遍历中的下标
visit[root]=true;//设置根已被访问
//判断根结点是否有左儿子,因为如果有左子树,
//中序遍历中根的左边的结点(左儿子)一定未被访问过,如果没有左儿子
//根左边存储的是其父结点一定已经被访问过了
if(root&&(!visit[root-1])){
q[i]->left=new Node(lorder[j]);//连接左儿子
q[j++]=q[i]->left;//将左儿子存到队列中,注意点:一定不要漏这一步
}
//判断根结点是否有右儿子,因为如果有右子树,
//中序遍历中根的右边的结点(右儿子)一定未被访问过,如果没有右儿子
//根右边存储的是其父结点一定已经被访问过了
if(root+1<lorder.size()&&(!visit[root+1])){
q[i]->right=new Node(lorder[j]);//连接右儿子
q[j++]=q[i]->right;//将右儿子存到队列中,注意点:一定不要漏这一步
}
//是否有小伙伴有这样的疑问为什么我上面只考虑有子结点的情况就行了呢?
//因为Node(char val):val(val),left(NULL),right(NULL),
//在构造结点时已自动初始其左右子树为空,自然无需额外的处理
}
}
dfs(q[0]);//基于深搜输入遍历序列
return 0;
}