剑指Offer学习总结-重建二叉树

剑指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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值