我们都知道,二叉树的先序和后序遍历序列可以确定二叉树的根,那么如果只通过先序或者后序序列是不能确定一个唯一的二叉树的,这是因为我们没有办法确定左右子树。而只根据二叉树的中序遍历序列,我们甚至连根都确定不了。但是通过后序与中序或是先序与中序,我们就能唯一的确定一个二叉树。这是因为,虽然先序或后序序列不能确定左右子树,但是我们可以通过得到的根在中序序列中将二叉树的左右子树确定。
描述
给定一个二叉树的中序与后序遍历结果,请你根据两个序列构造符合这两个序列的二叉树。
例如输入[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;
}