zoukankan      html  css  js  c++  java
  • 二叉树2

    二叉树2

    复习:写树的算法,关键思路如下:
    把题目的要求细化,搞清楚根节点应该做什么,然后剩下的事情抛给前/中/后序的遍历框架就行了,千万不要跳进递归的细节里

    leetcode654

    image-20210204132602996

    本题是依据条件构造一个特殊二叉树,构造二叉树最简单的方法就是递归,甚至题目中已经有了递归的意思
    依据题目,对于根节点root应该做什么:首先找到那个值,构造root,接着左子树找到值,右子树再找到值(不考虑细节,递归框架就是这个)
    详细一点就是:
    我们肯定要遍历数组找到最大值maxVal,把根节点root做出来,然后对maxVal左边的数组和右边的数组进行递归调用,作为root的左右子树。

    用伪码表示出来就是:

    TreeNode constructMaximumBinaryTree(int[] nums) {
        if (nums is empty) return null;
        // 找到数组中的最大值
        int maxVal = Integer.MIN_VALUE;
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > maxVal) {
                maxVal = nums[i];
                index = i;
            }
        }
    
        TreeNode root = new TreeNode(maxVal);
        // 递归调用构造左右子树
        root.left = constructMaximumBinaryTree(nums[0..index-1]);
        root.right = constructMaximumBinaryTree(nums[index+1..nums.length-1]);
        return root;
    }
    

    注意这里分割数组,利用的是输入数组和两个index,还是双指针
    我们需要最大值和最大值在数组中的位置

    /* 主函数 */
    TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }
    
    /* 将 nums[lo..hi] 构造成符合条件的树,返回根节点 */
    TreeNode build(int[] nums, int lo, int hi) {
        // base case
        if (lo > hi) {
            return null;
        }
    
        // 找到数组中的最大值和对应的索引
        int index = -1, maxVal = Integer.MIN_VALUE;
        for (int i = lo; i <= hi; i++) {
            if (maxVal < nums[i]) {
                index = i;
                maxVal = nums[i];
            }
        }
    
        TreeNode root = new TreeNode(maxVal);
        // 递归调用构造左右子树
        root.left = build(nums, lo, index - 1);
        root.right = build(nums, index + 1, hi);
    
        return root;
    }
    

    leetcode105: 通过前序和中序遍历结果构造二叉树

    本题是面试常见题目
    image-20210204134230567

    函数签名:

    TreeNode buildTree(int[] preorder, int[] inorder);
    

    类似上一题,我们要想办法确定根节点的值,把根节点做出来,然后递归构造左右子树即可

    前序遍历和中序遍历的结果有什么特点?

    void traverse(TreeNode root) {
        // 前序遍历
        preorder.add(root.val);
        traverse(root.left);
        traverse(root.right);
    }
    
    void traverse(TreeNode root) {
        traverse(root.left);
        // 中序遍历
        inorder.add(root.val);
        traverse(root.right);
    }
    

    这样的遍历顺序差异,导致了preorderinorder数组中的元素分布有如下特点:
    image-20210204134538376

    所以要做的是把相应的子树对应的值 喂给 子树
    找到根节点是很简单的,前序遍历的第一个值preorder[0]就是根节点的值,关键在于如何通过根节点的值,将preorderpostorder数组划分成两半,构造根节点的左右子树?
    换句话说,对于以下代码中的?部分应该填入什么:

    /* 主函数 */
    TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder, 0, preorder.length - 1,
                     inorder, 0, inorder.length - 1);
    }
    
    /* 
       若前序遍历数组为 preorder[preStart..preEnd],
       后续遍历数组为 postorder[postStart..postEnd],
       构造二叉树,返回该二叉树的根节点 
    */
    TreeNode build(int[] preorder, int preStart, int preEnd, 
                   int[] inorder, int inStart, int inEnd) {
        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal = preorder[preStart];
        // rootVal 在中序遍历数组中的索引
        int index = 0;
        for (int i = inStart; i <= inEnd; i++) {
            if (inorder[i] == rootVal) {
                index = i;
                break;
            }
        }
    
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(preorder, ?, ?,
                          inorder, ?, ?);
    
        root.right = build(preorder, ?, ?,
                           inorder, ?, ?);
        return root;
    }
    

    通过root的值可以把inorder成功分成左子树部分和右子树部分,通过这个长度,可以把preorder分成左子树部分和右子树部分

    image-20210204134951461 image-20210204135022897
    int leftSize = index - inStart;
    
    root.left = build(preorder, preStart + 1, preStart + leftSize,
                      inorder, inStart, index - 1);
    
    root.right = build(preorder, preStart + leftSize + 1, preEnd,
                       inorder, index + 1, inEnd);
    

    那么全部代码就是:

    TreeNode build(int[] preorder, int preStart, int preEnd, 
                   int[] inorder, int inStart, int inEnd) {
    
        if (preStart > preEnd) {
            return null;
        }
    
        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal = preorder[preStart];
        // rootVal 在中序遍历数组中的索引
        int index = 0;
        for (int i = inStart; i <= inEnd; i++) {
            if (inorder[i] == rootVal) {
                index = i;
                break;
            }
        }
    
        int leftSize = index - inStart;
    
        // 先构造出当前根节点
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(preorder, preStart + 1, preStart + leftSize,
                          inorder, inStart, index - 1);
    
        root.right = build(preorder, preStart + leftSize + 1, preEnd,
                           inorder, index + 1, inEnd);
        return root;
    }
    

    leetcode106:就是后序遍历与中序遍历结果来构造二叉树

    思路与上一题完全一样,画个图就可以

    TreeNode build(int[] inorder, int inStart, int inEnd,
                   int[] postorder, int postStart, int postEnd) {
    
        if (inStart > inEnd) {
            return null;
        }
        // root 节点对应的值就是后序遍历数组的最后一个元素
        int rootVal = postorder[postEnd];
        // rootVal 在中序遍历数组中的索引
        int index = 0;
        for (int i = inStart; i <= inEnd; i++) {
            if (inorder[i] == rootVal) {
                index = i;
                break;
            }
        }
        // 左子树的节点个数
        int leftSize = index - inStart;
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(inorder, inStart, index - 1,
                            postorder, postStart, postStart + leftSize - 1);
    
        root.right = build(inorder, index + 1, inEnd,
                            postorder, postStart + leftSize, postEnd - 1);
        return root;
    }
    
  • 相关阅读:
    Leetcode 1711. 大餐计数(桶)
    macOS下安装powerline-status失败(报错Could not install packages due to an EnvironmentError)
    macOS下安装oh my zsh失败(报错Connection refused)
    Chapter 10 Operator Overloading
    牛客IOI周赛26-普及组 B. 子序列(int128)
    关于状压DP中子集状态的枚举
    2021蓝桥杯省赛第一场C/C++A组 试题E:回路计数(状压DP)
    AcWing 2879. 画中漂流(简单DP)
    Leetcode 525. 连续数组(前缀和性质/map)
    牛客小白月赛34 B. dd爱探险(状压DP)
  • 原文地址:https://www.cnblogs.com/shiji-note/p/14375175.html
Copyright © 2011-2022 走看看