二叉树的遍历有先序、中序、后序遍历三种,已知中序和另外一种即可得到完整且唯一的二叉树。
本文通过递归的方法,通过先序和中序序列得到原来的二叉树。
还原的原理很简单,先序序列的每个结点都可以看作根结点,并且是根、左、右的顺序,而中序遍历的顺序为左、根、右,因此根据先序序列可以定位中序中的根结点,并且在这个根结点左侧的为左子树,右侧为右子树。
接下来可以按照当前根结点向左递归生成左子树,向右递归生成右子树,注意递归时的范围,例如向左递归,应该是从序列的0号位置到当前根结点位置的左侧,由于索引从0开始,因此根结点的编号就是要遍历的元素个数。
向右遍历的时候注意要在先序序列中排除掉递归左子树时已经使用的元素,由于先序是根、左、右的顺序,因此在定位根结点,向后递归 i 次(i是根结点在中序序列中的索引,也就是要递归解决的元素个数)后,应该在先序序列中排除根结点后面的i个点,从这里开始取到的才是右子树的结点。
根据上面的规则计算需要遍历的长度,当长度=0时,说明当前元素是度为0的结点,递归返回。
具体代码如下:其中还包含了
#include <iostream>
#include <stdio.h>
using namespace std;
#define MAXSIZE 50
typedef struct TreeNode *BinTree;
struct TreeNode{
char data;
BinTree left;
BinTree right;
};
/*
@param
pre 先序序列
med 中序序列
len 查找长度
*/
BinTree createBinTree(char *pre, char *med, int len){
if (len == 0)
{
// 遍历到了度0结点的左子树或右子树,直接返回
return NULL;
}
BinTree T = (BinTree)malloc(sizeof(struct TreeNode));
int i; // 表示当前中序的根结点位置,也表示当前根结点左侧的结点个数(左子树)。
T->data = *pre; // 从先序序列中取出根结点
for (i = 0; i < len; i++) {
if (*pre == *(med + i)) break; // 在中序序列中定位根结点
}
// 递归左子树,pre指针后移来排除已经找到的根结点,下一次查找的范围为当前根结点之前(左子树),而这里的i就是中序序列中
// 根结点左侧的元素个数,因此len传入i。
T->left = createBinTree(pre + 1, med, i);
// 递归右子树,由于左子树递归时已经使用了pre中的从根向后数的i个结点,因此要跳过这i个结点,加上根结点,一共i+1个结点,而
// 中序序列中要查找的长度为当前根的右侧和上一次根结点之间的结点,上一次根结点在i处存入了len传入,因此要查找的长度为len-i-1。
T->right = createBinTree(pre + i + 1, med + i + 1, len - i - 1);
return T;
}
void prePrint(BinTree T){
if (T != NULL){
printf("%c", T->data);
}
else{
return;
}
prePrint(T->left);
prePrint(T->right);
}
void medPrint(BinTree T){
if (T == NULL) return;
medPrint(T->left);
printf("%c", T->data);
medPrint(T->right);
}
void postPrint(BinTree T){
if (T == NULL) return;
postPrint(T->left);
postPrint(T->right);
printf("%c", T->data);
}
/*
测试序列
9
ABDFGHIEC
FDHGIBEAC
*/
int main(){
int N = 0;
char pre[MAXSIZE], med[MAXSIZE];
BinTree T = NULL;
scanf("%d", &N);
scanf("%s \n %s", pre, med);
T = createBinTree(pre, med, N);
prePrint(T);
cout << endl;
medPrint(T);
cout << endl;
postPrint(T);
cout << endl;
return 0;
}