- 题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}。二叉树结点的定义如下:
struct BinaryTreeNode{ int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; };
-
分析:
从此题开始,每个题在写代码之前都要按照之前的结题总结中的思路进行分析。首先是通过举例理解问题,体中也给出了一个例子,也就是根据二叉树的前序遍历和中序遍历来得到这个二叉树。接下来可以通过画图来实现这个重建二叉树的过程,从而发现其中的规律:根据二叉树的前序遍历可知,前序遍历中的每个结点都是一个子树的根结点;而在中序遍历序列中,根结点在序列的中间,左子树位于其左边,右子树位于其右边。最后试着能不能将复杂的问题分解成简单的问题。
通过题目中所给的例子,前序遍历中第一个数字1就是根结点的值,根据中序遍历,找到根结点的值的位置。根据中序遍历的特点,在根结点值1前边的3个数字都是左子树结点的值,位于1后边的数字都是右子树结点的值。那么,在前序遍历中,根结点1后边的3个数字就是左子树结点的值,再后边的所有数字都是右子树结点的值。这样,我们就在前序遍历和中序遍历两个序列中,分别找到了左子树、右子树对应的前序遍历的子序列和中序遍历的子序列。接下来,就可以用递归的方法来分别构建左子树和右子树了,过程如下图。
简单总结:从前序中确定根节点,然后在中序中找到根,从而将中序序列分为左右子树的中序遍历序列;再根据左右子树节点的个数,在前序遍历中将前序序列分为左右子树的前序遍历序列,接下来就是递归过程。
接下来还有一点要考虑的就是鲁棒性了,通过列举各种可能的测试用例来考虑。比如几种特殊的情况是否会导致程序崩溃?1、输入序列的指针为空或长度length为0;2、输入的序列不匹配;3、以及用各种不同形状的二叉树检验程序正确性(完全二叉树、普通二叉树、所有结点只有左(右)孩子的树)
代码如下:BinaryTreeNode* ConstructBinaryTree(int* preorder, int* inorder, int length) { // 输入空指针或长度小于等于0时返回NULL if (preorder == NULL || inorder == NULL || length <= 0) return NULL; // 创建根结点,前序序列中的第一个值为根结点的值 BinaryTreeNode* root = new BinaryTreeNode(); root->m_nValue = preorder[0]; root->m_pLeft = NULL; root->m_pRight = NULL; // 只有一个结点的情况 if (length == 1 && *preorder == *inorder) return root; else if (length == 1 && *preorder != *inorder) throw exception("Invalid input"); // 在中序序列中找到根结点的位置 int* rootinorder = inorder; int inorderlength = 0; while (*rootinorder != root->m_nValue && inorderlength < length) { ++rootinorder; ++inorderlength; } if (inorderlength == length && *rootinorder != root->m_nValue) throw exception("Invalid input"); int leftlength = rootinorder - inorder; int rightlength = length - leftlength - 1; if (leftlength > 0) { //构建左子树 root->m_pLeft = ConstructBinaryTree(preorder+1,inorder,leftlength); } if (rightlength > 0) { //构建右子树 root->m_pRight = ConstructBinaryTree(preorder+leftlength+1,rootinorder+1,rightlength); } return root; }