重建二叉树
1、题目
输入某二叉树的前序遍历和中序遍历序列的结果,请重建该二叉树,假设输入的前序遍历和中序遍历的结果中都不含重复的数字。前序遍历序列:{1,2,4,7,3,5,6,8},中序遍历序列:{4,7,2,1,5,3,8,6}
输入参数:前序遍历序列、中序遍历序列、序列的长度
输出结果:二叉树的根节点指针,或者为空
2、解题
首先,一个二叉树节点的定义如下,包括数据域和两个指向孩子节点的指针
struct BinaryTreeNode{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* mpRight;
};
其次,分析前序遍历序列和中序遍历序列。
前序遍历序列的排列为:根节点 —— 左子树 —— 右子树
中序遍历序列的排列为:左子树 —— 根节点 —— 右子树
所以,解题的核心步骤为:
- 在前序遍历序列找到根节点
- 在中序遍历序列找到根节点的位置
- 通过递归调用构建左右子树
- 返回根节点
3、鲁棒性
本题目的关键在于提高代码的鲁棒性。
鲁棒性(Robust)的定义:指程序能够判断输入是否合乎规范要求,并对不符合规范的输入进行合理的处理
提高代码鲁棒性的方法:防御性编程
防御性编程的定义:指遇见在什么地方出现问题,并为这些可能的问题制定处理方式
本题需要考虑三个地方
- 传入的前序、中序遍历序列、长度这三个参数的正确性
- 二叉树只有一个节点,输入序列的正确性
- 在中序遍历序列中无法找到根节点的情况
4、完整代码
BinaryTreeNode* construct(int* preorder, int* inorder, int length){
//第一次鲁棒性判断
if(preorder == null || inorder == null || length <= 0)
return null;
return constructCore(preorder, preorder+length-1, inorder, inorder+length-1);
}
BinaryTreeNode* constructCore
( int* startPreorder, int* endPreorder,
int* startInorder, int* endInorder
)
{
//先创建二叉树的根节点,并进行初始化
int rootValue = startPreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();
root->m_nValue = rootValue;
root->m_pLeft = root->m_pRight = nullptr;
//第二次鲁棒性判断
if (startPreorder == endPreorder) {
if (startInorder == endInorder && *startPreorder == *startInorder)
return root;
else
throw std::exception("Invalid input.");
}
//开始编写核心代码
int* rootInorder = startInorder;
while (rootInorder <= endInorder && *rootInorder != rootValue)
rootInorder++;
//第三次鲁棒性判断
if (rootInorder > endInorder)
throw std::exception("Invalid input.");
int leftLength = rootInorder - startInorder;
int* leftPreorderEnd = startPreorder + leftLength;
//开始构建左子树和右子树
if (leftLength > 0) {
root->m_pLeft = constructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
}
if (leftLength < endPreorder - startPreorder) {
root->m_pRight = constructCore(leftPreorderEnd + 1, endPreorder, rootInorder + 1, endInorder);
}
//最后返回根节点
return root;
}
5、注意点
- 构建左右子树的时候需要记得将其赋值给根节点的两个孩子指针
- 创造二叉树的根节点时,需将其初始化
- 最后记得返回根节点