7.剑指Offer之重建二叉树

1.题目描述:

  输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回根结点。

2.解题思路:

2.1前序遍历

在这里插入图片描述
  对于当前节点,先输出该节点,然后输出他的左孩子,最后输出他的右孩子。以上图为例,递归的过程如下:
(1):输出 1,接着左孩子;
(2):输出 2,接着左孩子;
(3):输出 4,左孩子为空,再接着右孩子;
(4):输出 7,左右孩子都为空,此时 2 的左子树全部输出,2 的右子树为空,此时 1 的左子树全部输出,接着 1 的右子树;;
(5):输出 3,接着左孩子;
(6):输出 5,左右孩子为空,此时 3 的左子树全部输出,接着右孩子;
(7):输出 6,接着左孩子;
(8):输出 8,左右孩子都为空,至此 1 的右子树全部输出,结束。

2.2中序遍历

  
在这里插入图片描述
对于当前结点,先输出它的左孩子,然后输出该结点,最后输出它的右孩子。以上图为例:
(1):1–>2–>4,4 的左孩子为空,输出 4,接着右孩子;
(2):7 的左孩子为空,输出 7,右孩子也为空,此时 2 的左子树全部输出,输出 2,2 的右孩子为空,此时 1 的左子树全部输出,输出 1,接着 1 的右孩子;
(3):3–>5,5 左孩子为空,输出 5,右孩子也为空,此时 3 的左子树全部输出,输出3,接着 3 的右孩子;
(4):6–>8,8的左右孩子为空,输出8,而 6 的右孩子为空,输出6,至此 1 的右子树全部输出,结束。

2.3思路

  树的遍历有三种:分别是前序遍历、中序遍历、后序遍历。前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}。本题是根据前序和中序遍历序列重建二叉树,我们可以通过一个具体的实例来发现规律,不难发现:前序遍历序列的第一个数字就是树的根结点。在中序遍历序列中,可以扫描找到根结点的值,则左子树的结点都位于根结点的左边,右子树的结点都位于根结点的右边。

  这样,我们就通过这两个序列找到了树的根结点、左子树结点和右子树结点,接下来左右子树的构建可以进一步通过递归来实现。

在这里插入图片描述

3.编程实现(Java):

public class RebuildBinaryTree_7 {
    public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
        /*根据前序遍历和中序遍历确定一棵二叉树*/
        //递归实现
        if (pre == null || in == null || pre.length == 0)
            return null;
        return reConstructBinaryTree(pre, in, 0, pre.length - 1, 0, in.length - 1);
    }

    public TreeNode reConstructBinaryTree(int[] pre, int[] in, int pre_begin,
                                          int pre_end, int in_begin, int in_end) {
        前序序列:从pre_begin到pre_end,  中序序列:从in_begin到in_end
        //递归结束条件
        if (pre_begin > pre_end || in_begin > in_end)
            return null;

        int rootValue = pre[pre_begin];
        TreeNode root = new TreeNode(rootValue);  //第一个节点就是根节点
        if (pre_begin == pre_end || in_begin == in_end){
            return root;
        }

        //在中序序列中,找到root,前面的就是左子树,右边的就是右子树
        int rootIn = in_begin; //root在中序序列中的位置,rootIn的值就是根的坐标
        while (rootIn <= in_end && in[rootIn] != rootValue){
            rootIn++;
        }

        int left_count = rootIn - in_begin; //左子树节点个数
        root.left = reConstructBinaryTree(pre, in, pre_begin + 1, pre_begin + left_count,
                in_begin, rootIn - 1);
        root.right = reConstructBinaryTree(pre, in, pre_begin + left_count + 1,
                pre_end, rootIn + 1, in_end);
        return root;
    }
}

4.根据前序遍历中序遍历推导树的结构

已知:
  前序遍历: GDAFEMHZ
  中序遍历: ADEFGHMZ
要画出这棵二叉树,怎么画呢?根据上面说的我们一步一步来……
  1.先看前序遍历,前序遍历第一个一定是根节点,那么我们可以知道,这棵树的根节点是G,接着,我们看中序遍历中,根节点一定是在中间访问的,那么既然知道了G是根节点,则在中序遍历中找到G的位置,G的左边一定就是这棵树的左子树,G的右边就是这棵树的右子树了。
  2.我们根据第一步的分析,大致应该知道左子树节点有:ADEF,右子树的节点有:HMZ。同时,这个也分别是左子树和右子树的中序遍历的序列。
  3.在前序遍历遍历完根节点后,接着执行前序遍历左子树,注意,是前序遍历,什么意思?就是把左子树当成一棵独立的树,执行前序遍历,同样先访问左子树的根,由此可以得到,左子树的根是D,第2步我们已经知道左子树是ADEF了,那么在这一步得到左子树的根是D,请看第4步。
  4.从第2步得到的中序遍历的节点序列中,找到D,发现D左边只有一个A,说明D的左子树只有一个叶子节点,D的右边呢?我们可以得到D的右子树有EF,再看前序遍历的序列,发现F在前,也就是说,F是先前序遍历访问的,则得到E是F的左子树,只有一个叶子节点。
  5.到这里,我们可以得到这棵树的根节点和左子树的结构了。如下图:
在这里插入图片描述
  6.接着看右子树,在第2步的右子树中序遍历序列中,右子树是HMZ三个节点,那么先看前序遍历的序列,先出现的是M,那么M就是右子树的根节点,刚好,HZ在M的左右,分别是它的左子树和右子树,因此,右子树的结构就出来了,到这里,我们可以得到整棵树的结构:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值