面试题7:重建二叉树
// 面试题7:重建二叉树
// 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输
// 入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,
// 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},则重建出
// 图所示的二叉树并输出它的头结点。(假装有图.jpg)
// 1
// / \
// 2 3
// / / \
// 4 5 6
// \ /
// 7 8
解题思路:
在Preorder->Inorder中依次找出根节点,随后根据Inorder左右子树的Inorder和数目,来确定左右子树的Preorder。
重复这个递归过程,知道找到叶结点,二叉树构建完毕。

编码过程还需要考虑各种输入情况,包括但不限于:
普通二叉树、完全二叉树、只有左子树、只有右子树、只有根节点、参数为nullptr、Preorder与Inorder输入错误等等。
伪代码:
if(Preorder与Inorder为空)
return nullptr;
根据Preorder建立根节点;
if(Preorder与Inorder长度均为1&&相等)
return 根节点;
else if(Preorder与Inorder长度均为1&&不相等)
throw exception;
while(Inorder未遍历完成&&Inorder元素!=根节点){
向后遍历;
}
if(遍历完成&&Inorder元素!=根节点)
throw exception;
if(左子树数目>0)
根节点->左子树=递归;
if(右子树数目>0)
根节点->右子树=递归
return 跟节点;
c/c++:
BinaryTreeNode* Construct(int* preorder, int* inorder, int length) {
//参数有效性校验
if (preorder == nullptr || inorder == nullptr || length <= 0)
return nullptr;
return ConstructCore(preorder, preorder + length - 1,
inorder, inorder + length - 1);
}
BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder,
int* startInorder, int* endInorder) {
//建立根节点
int rootValue = startInorder[0];
BinaryTreeNode* root = new BinaryTreeNode();
root->m_nValue = rootValue;
root->m_pLeft = nullptr;
root->m_pRight = nullptr;
//只有根节点,或者递归到达叶结点,返回
if (startPreorder == endPreorder) {
if (startInorder == endInorder
&&*startPreorder == *startInorder)
return root;
else
throw std::exception("Invalid Input!");
}
//在Inorder中寻找根节点的值
int* rootInorder = startInorder;
while (rootInorder <= endInorder&&*rootInorder != rootValue)
++rootInorder;
//Inorder遍历完仍未发现根节点的值,抛出异常
if (rootInorder == endInorder&&*rootInorder != rootValue)
throw std::exception("Invalid Input!");
int leftLength = rootInorder - startInorder;
int* leftEndPreorder = startPreorder + leftLength;
//根据左子树的preorder和inorder递归构建左子树
if (leftLength > 0)
root->m_pLeft=ConstructCore(startPreorder + 1, leftEndPreorder,
startInorder, rootInorder - 1);
//递归构建右子树
if (leftLength < endInorder - startInorder)
root->m_pRight=ConstructCore(leftEndPreorder + 1, endPreorder,
rootInorder + 1,endInorder);
//最后一定不要忘记返回
return root;
}
关于20-26行的代码,我刚开始想的是直接
if(startPreorder==endPreorder&&startInorder==endInorder&&*startPreorder==*startInorder)
,然后返回根节点,但是这样写的话,如果两次根节点的值不一致,就要多出很多判断
else if(startPreorder==endPreorder&&startInorder==endInorder&&*startPreorder!=*startInorder)
throw std::exception("");
else{正常执行}
所以像作者那么写看似多了if判断,实际上代码会更简洁,可读性更高,实际也不需要判断太多。
还有后面的定义leftLength和leftEndPreorder,可以减少重复的指针运算,也值得我们学习。

浙公网安备 33010602011771号