【剑指Offer】个人学习笔记_ 07_重建二叉树

目录

刷题日期:20:4827 星期一2021年3月15日

个人刷题记录,代码收集,来源皆为leetcode

经过多方讨论和请教,现在打算往Java方向发力

主要答题语言为Java

题目:

剑指 Offer 07. 重建二叉树

难度中等360

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

限制:

0 <= 节点个数 <= 5000

初始解答:

二叉树的题还是第一次遇到,书本中的代码也有一页多,自己先尽量思考大致的思路,了解判断根节点,左右子树的方法,然后大概想出来思路,参考别人的代码完成。

前序遍历的第一个数字是根节点;

中序遍历根节点之前的都是左子树;

之后的都是右子树;

一直循环,再复杂多层的情况目前还想不太明白。

要使用递归不断确定到最下层的左子树、双亲、右子树。

参考了Ant的评论:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //题目已经确定了函数名和输入的参数,那么想要递归就不重新定义函数
        //而是直接把题目作为递归的那个函数
        int n = preorder.length; //判断遍历结果长度
        if (n == 0)
            return null;  //为空返回空
        int rootVal = preorder[0], rootIndex = 0; //否则第一步先找到根节点,确定参数
        for(int i = 0; i <n; i++) {
            if(inorder[i] == rootVal) {
                rootIndex = i;
                break;  //在中序遍历中找到根节点的位置并记录
            }
        }
        TreeNode root = new TreeNode(rootVal);
        //下面的两句是核心
        root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + rootIndex), Arrays.copyOfRange(inorder, 0, rootIndex));

        /* Arrays.copyOfRange(T[ ] original,int from,int to)
        //将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组。
        注意这里包括下标from,不包括上标to */
        root.right = buildTree(Arrays.copyOfRange(preorder, 1 + rootIndex, n), Arrays.copyOfRange(inorder, rootIndex + 1, n));

        return root;
    }
}

执行结果:效率很是一般,难道是用了递归的缘故吗,但是这种解决树的问题,不就应该用递归么。

通过

显示详情

执行用时:14 ms, 在所有 Java 提交中击败了14.42%的用户

内存消耗:88.7 MB, 在所有 Java 提交中击败了5.04%的用户

知识点:

  • 前序遍历列表:第一个元素永远是 【根节点 (root)】
  • 中序遍历列表:根节点 (root)【左边】的所有元素都在根节点的【左分支】,【右边】的所有元素都在根节点的【右分支】

算法思路:

  1. 通过【前序遍历列表】确定【根节点 (root)】
  2. 将【中序遍历列表】的节点分割成【左分支节点】和【右分支节点】
  3. 递归寻找【左分支节点】中的【根节点 (left child)】和 【右分支节点】中的【根节点 (right child)】

试试看有没有效率更高的。

参考方法二:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //直接返回新建立函数的结果est:establish
        return est(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }
    public TreeNode est(int[] preorder, int l1, int r1, int[] inorder, int l2, int r2) {
        if(l1>r1||l2>r2) return null;
        int i = l2;
        while(inorder[i]!=preorder[l1]) {
            i++;
        }
        TreeNode root = new TreeNode(preorder[l1]);
        root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-2);
        root.right = est(preorder,l1+i-l2+1,r1,inorder,i+1,r2);
        return root;
    }
}

但是输入:[3,9,20,15,7] [9,3,15,20,7]

输出

[3,null,20,null,7]

差别

预期结果

[3,9,20,null,null,15,7]

与预期答案并不相同,寻找问题所在。

少的是左子树的9,判断错误在left节点语句上,果然排查发现最后的i-1码成了i-2,所以把9给跳过了,修正便可。

root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-2);//错误代码
root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-1);//正确代码

学习他人:

方法一:

AntL1 2020-02-13

题目函数直接递归

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        if (n == 0)
            return null;
        int rootVal = preorder[0], rootIndex = 0;
        for (int i = 0; i < n; i++) {
            if (inorder[i] == rootVal) {
                rootIndex = i;
                break;
            }
        }
        TreeNode root = new TreeNode(rootVal);
        root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + rootIndex), Arrays.copyOfRange(inorder, 0, rootIndex));
        root.right = buildTree(Arrays.copyOfRange(preorder, 1 + rootIndex, n), Arrays.copyOfRange(inorder, rootIndex + 1, n));

        return root;
    }
}

方法二:

SilenL12021-03-02

java 递归 不用HashMap

重新建立函数进行递归。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return help(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
    }
    public TreeNode help(int[] preorder,int l1,int r1,int[] inorder,int l2,int r2){
        if(l1>r1||l2>r2) return null;
        int i =l2;
        while(inorder[i]!=preorder[l1]){//在中序数组中去寻找根节点
            i++;
        }
        TreeNode root = new TreeNode(preorder[l1]);
        root.left = help(preorder,l1+1,l1+i-l2,inorder,l2,i-1);
        root.right = help(preorder,l1+i-l2+1,r1,inorder,i+1,r2);
        return root;
    }
}

help函数。

方法三:

作者:jyd
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-di-gui-fa-qin/
来源:力扣(LeetCode)

用HashMap,代码:

class Solution {
    int[] preorder;
    HashMap<Integer, Integer> dic = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        for(int i = 0; i < inorder.length; i++)
            dic.put(inorder[i], i);
        return recur(0, 0, inorder.length - 1);
    }
    TreeNode recur(int root, int left, int right) {
        if(left > right) return null;                          // 递归终止
        TreeNode node = new TreeNode(preorder[root]);          // 建立根节点
        int i = dic.get(preorder[root]);                       // 划分根节点、左子树、右子树
        node.left = recur(root + 1, left, i - 1);              // 开启左子树递归
        node.right = recur(root + i - left + 1, i + 1, right); // 开启右子树递归
        return node;                                           // 回溯返回根节点
    }
}

注意:本文方法只适用于 “无重复节点值” 的二叉树。

总结

以上就是本题的内容和学习过程了,虽然有很多简短的方法,但是书中进行替换的思路是值得学习的。

欢迎讨论,共同进步。

上一篇:vb.net 教程 11-1 打印组件 2 PrintDialog 1


下一篇:2.8