代码随想录算法训练营第十八天|513.找树左下角的值、112. 路径总和、113.路径总和ii、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树

代码随想录算法训练营第十八天|513.找树左下角的值、112. 路径总和、113.路径总和ii、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

题目链接

问题简述:找出最下面一层元素最左面的元素值。

思考:直接用队列层次遍历比较简单,递归较为复杂,和获得所有路径的题有点相似。然后层次遍历其实就是广度优先,递归就是深度优先。递归法哪种遍历都可以,毕竟哪种遍历都是先遍历左结点,再右结点。

层次遍历算法思路:队列存入每层元素。不断将队头元素值存入List,再将队头的左右节点再次存入List,最后推出队头。此时队列中为下一层元素,不断循环,记录每层的第一个元素。最终获得最后一层的第一个元素。

import java.util.ArrayDeque;

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        ArrayDeque<TreeNode> dq = new ArrayDeque<>();
        dq.add(root);
        //存储每层第一个元素
        int first = 0;
        while (!dq.isEmpty()){
            int size = dq.size();
            first = dq.getFirst().val;
            for (int i = 0; i < size; i++) {
                if(dq.getFirst().left != null) dq.add(dq.getFirst().left);
                if(dq.getFirst().right != null)dq.add(dq.getFirst().right);
                dq.removeFirst();
            }
        }
        return first;
    }
}

递归算法思路:递归遍历二叉树,同时记录每个节点深度,当遍历到叶子结点的时候,判断当前结点深度是否为第一次出现的最大深度,如果是则更新返回结果。然后依次遍历左右结点,直到遍历完所有叶子结点。

class Solution {
    //记录最终结果
    int res = Integer.MIN_VALUE;
    //记录最大深度
    int maxdeep = 0;
    //记录当前深度
    int deep = 0;
    public int findBottomLeftValue(TreeNode root) {
        deep++;
        //到叶子结点时
        if (root.left == null && root.right == null){
            //如果当前深度大于最大深度,则更改res,并更改最大深度       
            if (deep > maxdeep){
                res = root.val;
                maxdeep = deep;
            }
            //如果跟结点左右为空,返回根结点的值
            return root.val;
        }
        if (root.left != null){
            findBottomLeftValue(root.left);
            //回溯
            deep--;
        }
        if (root.right != null){
            findBottomLeftValue(root.right);
            //回溯
            deep--;
        }
        return res;
    }
}

112. 路径总和、113.路径总和ii

题目链接

题目链接

问题简述:求出二叉树是否存在某个路径和;求出二叉树路径和为某个值的所有路径。

思考:和遍历所有路径很像,关键是学会回溯。

112算法思路:先序遍历每一个结点,记录sum为遍历过路径的和,如果遍历到叶子结点时,将判断sum是否等于tar getSum。如果不是叶子结点,则遍历左结点,递归添加左面结点值,判断所有路径是否满足要求后进行回溯,将sum剪去当前节点的值。再递归添加右面结点,判断所有路径没有满足要求后,最后返回fasle。

import java.lang.annotation.Target;
import java.util.List;

class Solution {
    //存储路径和
    int sum = 0;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) return false;
        //sum中加入当前节点值
        sum += root.val;
        //到达叶子结点
        if (root.left == null && root.right == null){
            if (sum == targetSum) return true;
        }
        //左面有结点先添加左面结点
        if (root.left != null){
            //递归添加左面结点添加
            if (hasPathSum(root.left, targetSum)) return true;
            //回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
            sum -= root.left.val;
        }
        if (root.right != null){
            //递归添加左面结点添加
            if (hasPathSum(root.right, targetSum)) return true;
            //回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
            sum -= root.right.val;
        }
        //遍历完了所有路径,仍未返回,则返回fasle
        return false;
    }
}

113算法思路:先序遍历每一个结点,将结点放入path,如果遍历到叶子结点时,判断路径和是否为目标值,如果是则记录路径。如果不是叶子结点,则遍历左结点,递归添加左面结点,存入所有路径后进行回溯。再递归添加右面结点,存入所有路径后进行回溯。最后返回。

import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        //用于存储最终结果
        List<List<Integer>> res = new ArrayList<>();
        //用于存储每条路径值的List
        List<Integer> path = new ArrayList<>();
        traversal(root, path, res, targetSum);
        return res;
    }

    public void traversal(TreeNode root, List<Integer> path, List<List<Integer>> res, int targetSum){
        if (root == null) return;
        //path中加入当前节点
        path.add(root.val);
        //到达叶子结点
        if (root.left == null && root.right == null){
            int sum = 0;
            for (int i: path) {
                sum += i;
            }
            if (sum == targetSum) res.add(new ArrayList<>(path));
            return;
        }
        //左面有结点先添加左面结点
        if (root.left != null){
            //递归添加左面结点添加
            traversal(root.left, path, res, targetSum);
            //回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
            path.remove(path.size() - 1);
        }
        if (root.right != null){
            //递归添加左面结点
            traversal(root.right, path, res, targetSum);
            //回溯到添加这个右结点之前
            path.remove(path.size() - 1);
        }
        //遍历完了所有路径
        return;
    }
}

105.从前序与中序遍历序列构造二叉树、106.从中序与后序遍历序列构造二叉树

105:题目链接

106:题目链接

问题简述:将无重复元素的前序与中序遍历序列构造二叉树;将无重复元素的中序与后序遍历序列构造二叉树。

思考:看了解析,直接定义一个指向新的preorder的头和尾的指针就好,不需要每次新定义一个数组了;其次想找到某个元素下标用map简单很多,只写了105题,以后再写优化后的106。

105算法思路:每次找到前序遍历第一个元素在中序序列中的位置,用这个位置把preorder和inorder的元素重新分割为左子树的preorder和inorder与右子树的preorder和inorder。然后递归链接跟结点的左右子树。直到遍历完所有节点。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //如果无元素,则返回空结点
        if (preorder.length == 0 && inorder.length == 0) return null;

        //创建当前元素结点
        TreeNode root = new TreeNode(preorder[0]);
      
        //记录跟结点在中序遍历位置
        int a = 0;
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == root.val){
                a = i;
                break;
            }
        }

        //左子树构造
        int[] newpreorder1 = new int[a];
        int[] newinorder1 = new int[a];
        //左子树新的前序数组赋值
        for (int i = 0; i < a; i++) {
            newpreorder1[i] = preorder[i + 1];
        }
        //左子树新的中序数组赋值
        for (int i = 0; i < a; i++) {
            newinorder1[i] = inorder[i];
        }
        root.left = buildTree(newpreorder1, newinorder1);


        //右子树构造
        int n = preorder.length - a - 1;
        int[] newpreorder2 = new int[n];
        int[] newinorder2 = new int[n];
        //右子树新的前序数组赋值
        for (int i = 0; i < n; i++) {
            newpreorder2[i] = preorder[a + 1 + i];
        }
        //左子树新的中序数组赋值
        for (int i = 0; i < n; i++) {
            newinorder2[i] = inorder[a + 1 + i];
        }
        root.right = buildTree(newpreorder2, newinorder2);

        return root;
    }
}

感想

我觉得今天很难过,很焦虑,几天没怎么学习;女朋友都找到了工作还在考虑我,我配吗?同龄人的朋友圈丰富多彩,考上研的旅游,没考上的有了工作,早早工作的也有了自己的乐趣生活;已经读研的也有时间旅游。我觉得我就是废物,想似。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值