编程之美 3.9:重建二叉树

缘由

偶然翻开那本编程之美,好!今天来一题

题干




思路

从题干中给出的例子来分析,要点有下面三个
  1. 直觉很强烈!递归。
  2. 前序的第一个字符是整棵树的根节点,那么在中序中以根节点为界,可以获得左子树与右子树
    如下:
    前序:abdcef
    中序:dbacef
    a为根节点,那么从中序中以a节点为界可以划分为两半,
    前序:a bd cef,由于是前序,又可以根据中序遍历来获得左右子树的个数。那么左子树的第一个就是根节点的左孩子,也就是b是a的左孩子,右子树的第一个也就是根节点的右孩子。
    中序:db a cef,db是左子树,cef为右子树。由此获得左子树有2个,右子树又有3个。
  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$ 


延伸问题

  1. 如果节点的value的字母相同。怎么办?如何构建出所有的解?
  2. 如何判定输入的前序和中序遍历是正确的?
  3. 如果知道前序和后序遍历的结果,能否构建出二叉树。
  4. 请设计测试用例

源代码

没有上传网盘
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值