剑指 Offer 07. 重建二叉树

今天通过这道题来研究研究前序遍历,中序遍历之间到底有什么关系
题目描述:

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

题目示例:
在这里插入图片描述

我先说一下解题思路,当然,你要是想彻底了解下面的过程,那么你应该要对二叉树的前中后序遍历了然于心,不是很清楚的,可以先读一下:《剑指 Offer 54. 二叉搜索树的第k大节点》,这道题目弄懂之后在回来看看这道题。

我们整个过程就是不停的通过前序遍历去找根节点,然后通过中序遍历去划分左右子树,我通过题目示例来演示一下:

  • 1.首先通过前序遍历确定第一个根节点(i=0):pre[0]=3
    然后通过中序遍历找到3在中序遍历中的位置:[9,3,15,20,7]
    所以我们就可以划分出第一棵子树:
    在这里插入图片描述

  • 2.在接续找下一棵树,首先你要心里清楚,以3位根节点的是我们要确定的第一棵子树,再接着找下一棵子树(i=1),pre[1]=9,也就是说下一棵我们要确定的树是以9为根节点,确定好了根节点,我们就要通过中序遍历确定左右子树,[9,3,15,20,7],可以发现9没有左右子树
    在这里插入图片描述

  • 3.再接着通过 前序遍历的序列确定下一个根节点(i=2)pre[2]=20
    在中序遍历的范围内找到20来划分左右子树:[9,3,15,20,7],于是树又进一步被我们划分为下图所示的样子:
    在这里插入图片描述

  • 4.再根据前序遍历接着找下一个根节点(i=3)pre[3]=15:
    [9,3,15,20,7]
    我们会发现15同样没有左右子树

  • 5.再根据前序遍历接着找下一个根节点(i=4)pre[4]=7:
    [9,3,15,20,7]

通过上面的步骤,其实不难发现我们一直在经历两个过程,在前序遍历序列找根节点,在中序遍历序列划分左右子树。
代码实现如下:

public class 剑指Offer07_重建二叉树 {

    Map<Integer, Integer> map;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int length = preorder.length;
        if (length <= 0) return null;
        if (length <= 1) return new TreeNode(preorder[0]);
        map = new HashMap<>();
        for (int i = 0; i < length; i++) {//找到中序遍历下对应的根节点位置
            map.put(inorder[i], i);
        }
        return dfs(preorder, 0, length - 1,
                0, length - 1);
    }

    /**
     * 递归的去构建二叉树
     *
     * @param preorder
     * @param inorder
     * @param pre_left  前序遍历范围 的第一个节点位置
     * @param pre_right 前序遍历范围的最后一个位置
     * @param in_left   中序遍历范围内的第一个位置
     * @param in_right  中序遍历范围内的最后一个位置
     * @return
     */
    public TreeNode dfs(int[] preorder, int pre_left, int pre_right,
                        int in_left, int in_right) {
        if (pre_left > pre_right) {//循环终止条件,说明根节点已经遍历结束了
            return null;
        }
        //确定根节点
        int pre_root = pre_left;//前序遍历下根节点的位置
        //构建根节点
        TreeNode root = new TreeNode(preorder[pre_root]);
        int in_root = map.get(preorder[pre_root]);//旨在判断左右子树的长度
        //获取左子树的长度
        int length_left = in_root - in_left;
        //开始递归的重复这个操作构建左右子树
        /**
         * 因为前序遍历左边的第一个节点我们已经用过了,所以要找下一个根节点
         * 同时我们也可以确定左子树的长度为:len_left
         */
        root.left = dfs(preorder, pre_left+1, pre_left + length_left,
                in_left, in_root - 1);

        root.right = dfs(preorder, pre_left + length_left + 1, pre_right,
                in_root + 1, in_right);
        return root;

    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZNineSun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值