我们有时会遇到给出一颗二叉树的前序遍历与中序遍历,或者中序遍历与后序遍历让我们给出这棵树的另外一种遍历的情况,这将是这篇博客的主要问题。
我们先给出一个例子,洛谷P1827,这是一道给出前序遍历与中序遍历要求出后序遍历的题目,这些遍历究竟是什么这里不在赘述,我们直接来看这道题的思路。
因为给出了前序遍历与中序遍历,我们有一种直觉就是要从根节点入手,
容易知道,先序遍历的第一个元素是整个树的根节点,我们可以再中序遍历中找到他,找到整个树的根节点之后,在中序遍历中,在根节点左边的就是这棵树的左子树,在根节点右边的就是这棵树的右子树,在左子树中,我们可以继续刚才的操作,找到左子树的根节点,将左子树再分为两棵树(如果他可以被分成两棵树的话),对于右子树也进行相同的操作。
下面是一个具体的例子
中序遍历:ABEDFCHG
前序遍历:CBADEFGH
易知,C是整个树的根节点,而这棵树的左子树的根节点就是B,左子树的左子树的根节点就是A,以此类推,直到左边到达了叶子节点。
下面给出具体的代码,在给出代码之前,要对其中的函数进行说明,函数说明请认真看完,下面的代码不会有太多注释
第一个函数是 int find(char ch) 函数,用来在中序遍历中寻找前序遍历所提供的根节点,以便于将中序遍历分成左右子树。这里使用循环来解决问题。
第二个函数是void dfs(int pre_start,int pre_end,int in_start,int in_end),四个参数分别为在某棵树中(可能是左子树或者右子树)先序遍历和中序遍历对于这棵树开始和结束的位置,解释一下:在例子中根节点是C,在中序遍历中的位置是5,对于左子树,中序遍历开始的位置就是0——4,前序遍历开始的位置就是1——5,
这里要对于这个5着重解释:
确定根节点的位置之后,我们知道这棵树的右子树只有两个节点,所以在前序遍历的最后两个元素就是这棵树右子树的元素,假设根节点的位置用root表示,5的来由是pre_end - (in_end - root)
继续对dfs函数进行说明,如果我们判断存在左子树,就对左子树进行dfs,如果判断存在右子树,就对右子树进行dfs,通过刚才的分析向其中传入参数,最后输出当前根节点的元素,因为是后序遍历,所以顺序一定是左右后
接下来是相应的代码
#include<stdio.h>
#include<string.h>
char in[30],pre[30];
int len;
int find(char ch)
{
for(int i = 0;i < len;i++)
{
if(in[i] == ch)
return i;
}
}
void dfs(int pre_start,int pre_end,int in_start,int in_end)//后序遍历,顺序为左右中
{
int root = find(pre[pre_start]);
if(root > in_start)//存在左子树
{
dfs(pre_start + 1,pre_end - (in_end - root),in_start,root - 1);
}
if(root < in_end)//存在右子树
{
dfs(pre_end - (in_end - root) + 1,pre_end,root + 1,in_end);
}
printf("%c",pre[pre_start]);
return ;
}
int main()
{
scanf("%s",in);
getchar();
scanf("%s",pre);
len = strlen(in);
dfs(0,len - 1,0,len - 1);
return 0;
}
对于给出中序遍历与后序遍历来确定前序遍历的问题,对应洛谷P1030,方法和函数与上面类似,如果上面能看懂,这个也应该迎刃而解,所以直接给出代码:
#include<stdio.h>
#include<string.h>
char t1[10];
char t2[10];
int len;
int find(char ch)
{
for(int i = 0;i < len;i++)
if(t1[i] == ch)
return i;
}
//相当于通过后序遍历求出节点,再通过节点位置将中序遍历分成两个部分,一个是左子树的中序遍历,一个是右子树的中序遍历
//通过元素数的计算把后续遍历分为左子树的后序遍历和右子树的后序遍历,再分别求左右子树的根节点
//判断左右子树是否存在是看节点位置和中序遍历头和尾的位置关系,比如 如果节点和中序遍历的头相重合,就说明这棵树没有
//左子树
void dfs(int len1,int root1,int len2,int root2)//t1的开始位置和结束位置,t2的开始位置和结束位置
{
int m = find(t2[root2]);
printf("%c",t2[root2]);
if(m > len1)//节点位置大于t1开始的位置,存在左子树
//左子树的开始仍为len1,结束为节点数减一的位置,对应的后序遍历开始仍为len2,结束为root2减去右子树元素数再减一
dfs(len1,m - 1,len2,root2 - (root1 - m) - 1);
if(m < root1)//节点的位置小于t1的结束位置,存在右子树
dfs(m + 1,root1,root2 - (m - len1),root2 - 1);
}
int main()
{
scanf("%s%s",t1,t2);
len = strlen(t1);
dfs(0,len - 1,0,len - 1);
return 0;
}
我们也可以建立树形结构,通过遍历的信息将这棵树还原出来,但是这里没有必要这样做。