剑指Offer学习总结-重建二叉树
本系列为剑指Offer学习总结,主要是代码案例的分析和实现:
书籍链接:http://product.dangdang.com/24242724.html
原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/
原作者博客链接有完整的项目代码下载。
重建二叉树
题目
题目:输入某二叉树的 前序遍历和中序遍历的结果,请重建出该二叉树。
假设输入的前序遍历和中序遍历的结果都不含重复的数值。
例如输入的前序遍历序列 {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 后面的数字都是右子
树结点的值。
由子在中序遍历序列中, 有 3 个数字是左子树结点的值, 因此左子树总共有 3 个左子结点.
同样, 在前序遍历的序列中, 根结点后面的 3 个数字就是 3 个左子树结点的值,
再后面的所有数字都是右子树结点的值。
这样我们就在前序遍历和中序遍历两个序列中, 分别找到 左右子树对应的子序列。
既然我们己经分别找到了左、 右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别去构建左右子树。 也就是说, 接下来的事情可以用递归的方法去完成。
解法思路分析:
在二叉树的前序遍历序列中, 第一个数字总是树的根结点的值。
但在中序遍历序列中, 根结点的值在序列的中间, 左子树的结点的值位于根结
点的值的左边, 而右子树的结点的俏位千根结点的值的右边 。
因此我们需要扫描中序遍历序列, 才能找到根结点的值。
得到根节点之后,我们可以区分出左右子树中序遍历序列和长度,
然后从前序遍历中推出相应的左右子树前序遍历,
接着我们分别对左右子树,进行同上的操作,直到左右子树都为Null为止。
//重建二叉树入口 参数 前序遍历 中序遍历 和 序列的长度
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 = NULL;
//是否只有一个节点
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 && *rootInorder != rootValue)
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;
}