缘由
偶然翻开那本编程之美,好!今天来一题题干
思路
从题干中给出的例子来分析,要点有下面三个- 直觉很强烈!递归。
- 前序的第一个字符是整棵树的根节点,那么在中序中以根节点为界,可以获得左子树与右子树
如下:
前序:abdcef
中序:dbacef
a为根节点,那么从中序中以a节点为界可以划分为两半,
前序:a bd cef,由于是前序,又可以根据中序遍历来获得左右子树的个数。那么左子树的第一个就是根节点的左孩子,也就是b是a的左孩子,右子树的第一个也就是根节点的右孩子。
中序:db a cef,db是左子树,cef为右子树。由此获得左子树有2个,右子树又有3个。
- 被根节点分割子序列,前序的还是前序,中序的还是中序。
代码
下面的代码是我自己写的,不敢保证完全正确。验证方式采用了构建后的二叉树再产生前序和中序,与输入进行比较。即可知道构建的正确与否。/**
* 根据二叉树的前序和中序,重新构建二叉树
* 选自编程之美:3.9 重建二叉树
* author:zy
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct NODE{
struct NODE *pLeft;
struct NODE *pRight;
char chValue;
} Node;
/**
* 因为要在不同的函数中使用,所以必须是全局变量
* 这是因为重建后的二叉树是不是
*/
static char *pPreOrderToCheck;
static char *pInOrderToCheck;
static char *temp;
/**
* 根据前序和中序遍历重新构建一个二叉树
*/
void Rebuild(char *pPreOrder,char *pInOrder,int nTreeLen,Node *pRoot){
/*永远忘掉的边界检查*/
if(pPreOrder == NULL || pInOrder == NULL){
return;
}
char pPre = pPreOrder[0];
char *pIn = pInOrder;
/*获得左子树的长度*/
int leftLength = 0;
while (1){
if (pPre == * pIn ){
break;
}
pIn++;
leftLength++;
}
int righLength = nTreeLen - leftLength - 1;//减去的1是根节点的长度
pRoot->chValue = pPre;
if (leftLength!=0){
//对左子树处理,取得左子树的前序和中序遍历的序列,再递归
Node *left = (Node *) malloc(sizeof(Node));
pRoot->pLeft = left;
char *leftPreString = (char *)malloc(sizeof(char)*(leftLength+1));
leftPreString = strncpy(leftPreString,pPreOrder+1,leftLength);
leftPreString[leftLength] = '\0';
char *leftInString = (char *)malloc(sizeof(char)*(leftLength+1));
leftInString = strncpy(leftInString,pInOrder,leftLength);
leftInString[leftLength] = '\0';
Rebuild(leftPreString,leftInString,leftLength,left);
}else{
pRoot->pLeft = NULL;
}
if (righLength!=0){
//对右子树处理,取得右子树的前序和中序遍历的序列,再递归
Node *right = (Node *) malloc(sizeof(Node));
pRoot->pRight = right;
char *rightPreString = (char *)malloc(sizeof(char)*(righLength+1));
rightPreString = strncpy(rightPreString,pPreOrder+leftLength+1,righLength);
rightPreString[righLength] = '\0';
char *rightInString = (char *)malloc(sizeof(char)*(righLength+1));
rightInString = strncpy(rightInString,pInOrder+leftLength+1,righLength);
rightInString[righLength] = '\0';
Rebuild(rightPreString,rightInString,righLength,right);
}else{
pRoot->pRight = NULL;
}
}
/**
* 得到一个二叉树的前序遍历
*/
void GetPreOrder(Node *root){
if(root!=NULL){
*temp++ = root->chValue;
}
if(root->pLeft != NULL){
GetPreOrder(root->pLeft);
}
if(root->pRight != NULL){
GetPreOrder(root->pRight);
}
}
/**
* 得到一个二叉树的中序遍历
*/
void GetInOrder(Node *root){
if(root->pLeft != NULL){
GetInOrder(root->pLeft);
}
if(root!=NULL){
*temp++ = root->chValue;
}
if(root->pRight != NULL){
GetInOrder(root->pRight);
}
}
void main(){
Node *root =(Node *) malloc(sizeof(Node));
char *pPreOrder = "abdcef";
char *pInOrder = "dbaecf";
int nTreeLen = strlen(pPreOrder);
Rebuild(pPreOrder,pInOrder,nTreeLen,root);
//为了验证,最直接的办法就是得到这个二叉树的前序和中序遍历
pPreOrderToCheck = (char *)malloc(nTreeLen+1);//多一个是为装'\0'
pInOrderToCheck = (char *)malloc(nTreeLen+1);//多一个是为装'\0'
temp = pPreOrderToCheck;
GetPreOrder(root);
temp = '\0';
if(strcmp(pPreOrder,pPreOrderToCheck)==0){
printf("构建完成:前序相同:%s\n",pPreOrderToCheck);
}else{
printf("前序不同,输入为:%s,构建的为:%s\n",pPreOrder,pPreOrderToCheck);
}
temp = pInOrderToCheck;
GetInOrder(root);
temp = '\0';
if(strcmp(pInOrder,pInOrderToCheck)==0){
printf("构建完成:中序相同:%s\n",pInOrderToCheck);
}else{
printf("前序不同,输入为:%s,构建的为:%s\n",pInOrder,pInOrderToCheck);
}
}
运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
构建完成:前序相同:abdcef
构建完成:中序序相同:dbaecf
asd@asd-desktop:~/workspace/test/src$
延伸问题
- 如果节点的value的字母相同。怎么办?如何构建出所有的解?
- 如何判定输入的前序和中序遍历是正确的?
- 如果知道前序和后序遍历的结果,能否构建出二叉树。
- 请设计测试用例
源代码
没有上传网盘