由二叉树的遍历序列确定二叉树

我们都知道,二叉树的先序和后序遍历序列可以确定二叉树的根,那么如果只通过先序或者后序序列是不能确定一个唯一的二叉树的,这是因为我们没有办法确定左右子树。而只根据二叉树的中序遍历序列,我们甚至连根都确定不了。但是通过后序与中序或是先序与中序,我们就能唯一的确定一个二叉树。这是因为,虽然先序或后序序列不能确定左右子树,但是我们可以通过得到的根在中序序列中将二叉树的左右子树确定。

描述

给定一个二叉树的中序与后序遍历结果,请你根据两个序列构造符合这两个序列的二叉树。

例如输入[2,1,4,3,5],[2,4,5,3,1]时,

根据中序遍历的结果[2,1,4,3,5]和后序遍历的结果[2,4,5,3,1]可构造出二叉树{1,2,3,#,#,4,5},如下图所示:

示例1

输入:

[1],[1]

返回值:

{1}

示例2

输入:

[2,1,4,3,5],[2,4,5,3,1]

返回值:

{1,2,3,#,#,4,5}

由于二叉树的一种定义就是使用递归,所以在还原二叉树时,我们也很容易想到递归。首先能通过先序或后序拿到根节点,然后通过中序遍历将左右子树区分开,接着递归建立根的左子树和右子树。

实现代码如下:

typedef struct TreeNode {
	int val;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

//根据遍历序列构造二叉树必须有两种序列,并且必须有中序序列,这是因为
//只有根据中序序列才能确定左右子树中的结点,根据先序或后序可以确定根节点
//并且由于二叉树的定义本身就是递归的,那么我们很容易想到使用递归来构造二叉树
//并且由于需要构造左子树和右子树,所以还要用到分治
//

//从中序后序遍历递归构造二叉树
//假设此算法能够成功构造二叉树,并且返回树的根节点
TreeNode* InorderAndPostorderBuildTree(int* inorder, int inlen, int* postorder, int postlen) {

	//我们需要从postorder中拿到根节点,所以当postlen为0时,也就没有新的节点了
	//所以跳出递归的条件就为postlen==0
	if (postlen) {
		//1.先建立根节点,后序遍历序列的最后一个元素就是根节点的值
		TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
		if (root == NULL) {
			perror("malloc");
			return NULL;
		}
		root->val = postorder[postlen - 1];

		//2.接下来递归建立左子树和右子树
		//首先需要将中序遍历和后序遍历分为左右子树两个部分,先在中序遍历中找到根节点
		int index;//记录根节点的下标
		for (int i = 0; i < inlen; i++) {
			if (inorder[i] == root->val) {
				index = i;
				break;
			}
		}
		//由于中序遍历先左子树,此时index的值就是左子树中节点的个数
		//那么右子树的节点的个数就为总结点数inlen-index-1
		root->left = InorderAndPostorderBuildTree(inorder, index, postorder, index);
		root->right = InorderAndPostorderBuildTree(inorder + index + 1, inlen - index - 1, postorder + index, inlen - index - 1);
		return root;
	}
	//如果没有新节点,那就返回NULL
	return NULL;

}

//先序中序遍历构造二叉树,思路与中序后序是一样的
TreeNode* InorderAndPreorderBuildTree(int* inorder, int inlen, int* preorder, int prelen) {

	//当先序序列长度为0,就说明没有新节点了
	if (prelen) {

		//与之前相似,先确定根节点,再递归建立左右子树
		TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
		if (root == NULL) {
			perror("malloc");
			return NULL;
		}
		root->val = preorder[0];

		//从中序中找到根节点的下标index,index的值就是左子树的结点个数
		int index;
		for (int i = 0; i < inlen; i++) {
			if (inorder[i] == root->val) {
				index = i;
				break;
			}
		}

		//递归建立左右子树
		root->left = InorderAndPreorderBuildTree(inorder, index, preorder + 1, index);
		root->right = InorderAndPreorderBuildTree(inorder + index + 1, inlen - index - 1, preorder + 1 + index, inlen - index - 1);
		return root;
	}
	//当没有新节点时,返回NULL
	return NULL;

}

 测试代码:

#include <stdio.h>
#include <stdlib.h>

int main() {


	int inorder[5] = { 2,1,4,3,5 };
	int preorder[5] = { 1,2,3,4,5 };
	TreeNode* root = InorderAndPreorderBuildTree(inorder, 5, preorder, 5);

	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值