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

参考资料:

posted @ 2018-08-04 23:53  朕蹲厕唱忐忑  阅读(160)  评论(0编辑  收藏  举报