面试题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,可以减少重复的指针运算,也值得我们学习。