算法学习day18

news2024/11/16 10:26:21

文章目录

      • 513.找树左下角的值
        • 递归
        • 迭代
      • 112 .路径总和
        • 递归
        • 迭代
      • 113.路径总和II
        • 递归
      • 106.从中序与后序遍历序列构造二叉树
        • 递归
      • 105.从前序与中序遍历序列构造二叉树
        • 卡尔递归版本
        • 递归优化
      • 总结

513.找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

img

输入: root = [2,1,3]
输出: 1

示例 2:

img

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,104]
  • -231 <= Node.val <= 231 - 1

递归

利用最大深度,判断是否时最后一层,每次更新最大深度,不断更新左侧节点的返回值,第一个大于最大深度的一定是左下角的值

  • 入参:根节点,递归深度
  • 终止条件:无子节点,且当前深度大于最大深度
  • 循环逻辑:迭代一层回溯一次
 class Solution {
    int maxDepth ,res ;
    public int findBottomLeftValue(TreeNode root) {
        maxDepth(root,1);
        return res ;
    }
    public void maxDepth(TreeNode root,int depth) {
        //终止条件,无子节点
        if(root.left == null && root.right == null) {
            //只有大于最大深度时才能更新最大深度,此时左右深度相同,第一个值就是左子树的值
            if(depth > maxDepth) {
                maxDepth = depth ;
                res = root.val ;
                return;
            }
        }
        //单层循环逻辑
        if(root.left!= null) {
            depth++;
            maxDepth(root.left,depth+1);
            depth--;
        }
        if(root.right!= null) {
            depth++;
            maxDepth(root.right,depth+1);
            depth--;
        }
    }

}

迭代

  • 层序遍历,每层的第一个值
class Solution {
    public int findBottomLeftValue(TreeNode root) {

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int res = root.val;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                //队列的第一个节点是左节点的值
                if(i == 0){
                    res = node.val;
                }

                if (node.left!= null) {
                    queue.offer(node.left);
                }
                if (node.right!= null) {
                    queue.offer(node.right);
                }
            }
        }
        return res;
    }

}

112 .路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

img

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

递归

  • 递归三部曲:入参和返回值(局部变量),终止条件,单层循环逻辑
  • 入参:节点,路径、返回结果
  • 终止条件:左右子节点为空,添加返回值
  • 循环条件:每循环一层,递归+回溯
class Solution {
    int sum = 0;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null) return false;
        sum += root.val;
        if(root.left == null && root.right == null) {
            return sum == targetSum;
        }
        if(root.left!= null){
            if(hasPathSum(root.left, targetSum)){
                return true;
            }
            sum -= root.left.val;
        }

        if(root.right!= null){
            if(hasPathSum(root.right, targetSum)){
                return true;
            }
            sum -= root.right.val;
        }
        return false;
    }
}

迭代

  • 参考二叉树的层序先序,借助栈+回溯,类似于递归

113.路径总和II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:

img

输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:

输入:root = [1,2], targetSum = 0
输出:[]

提示:

  • 树中节点总数在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

递归

  • 入参:每个节点,目标值,返回结果,路径
  • 终止条件:sum == targetSum
  • 循环逻辑:每次回溯的节点,为当前递归的那个节点(递归参数里的节点)
class Solution {
    int sum = 0 ;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        List<Integer> paths = new ArrayList<>();
        dfs(root, targetSum, res, paths);
        return res;
    }

    public void dfs(TreeNode root,int targetSum , List<List<Integer>> res,  List<Integer> paths){
        //终止条件
        if (root == null) {
            return;
        }
        sum += root.val;
        paths.add(root.val);

        if (root.left == null && root.right == null) {
            if (sum == targetSum) {
                res.add(new ArrayList<>(paths));
            }
            return;
        }
        if(root.left != null){
            dfs(root.left,targetSum, res, paths);
            sum -= root.left.val;
            paths.remove(paths.size() - 1);
        }
        if(root.right!= null){
            dfs(root.right,targetSum, res, paths);
            sum -= root.right.val;
            paths.remove(paths.size() - 1);
        }
    }
}

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

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:

img

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

递归

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        //终止条件1
        if (postorder.length == 0 || inorder.length == 0) {
            return null;
        }
        //创建根节点
        TreeNode root = new TreeNode(postorder[postorder.length - 1]);
        //终止条件2
        if (postorder.length == 1) {
            return root;
        }
        //通过中序找到切割点index
        int index = 0 ;
        for (int i = 0; i < inorder.length; i++) {
            if(inorder[i] == root.val) {
                index = i ;
                break;
            }
        }
        //切中序数组  始终左闭右开
        int[] leftInOrderSlice =subArray(inorder,0,index);

        int[] rightInOrderSlice =subArray(inorder,index+1,inorder.length);

        //切后序数组  以中序数组的左半部分的数组长度,切先序数组的左半部分
        int[] leftPreSlice = subArray(postorder,0,leftInOrderSlice.length);

        //切后序数组  以中序数组的右半部分的数组长度,切先序数组的右半部分
        int[] rightPreSlice = subArray(postorder,leftPreSlice.length,leftPreSlice.length+rightInOrderSlice.length);

        //创建左子树
        root.left = buildTree(leftInOrderSlice,leftPreSlice);
        //创建右子树
        root.right = buildTree(rightInOrderSlice,rightPreSlice);
        return root;
    }
    public int[] subArray(int[] array, int start, int end){
        if(start>=end){
            return new int[0];
        }
        int[] result = new int[end-start];
        for (int i = start; i < end; i++) {
            result[i-start] = array[i];
        }
        return result;
    }
}

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

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

img

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorderinorder无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

卡尔递归版本

  • 留个步骤
    • 终止条件,任意数组为空
    • 前序数组为1,结束遍历,返回根
    • 使用前序数组的第一个节点,切割中序数组
    • 分割中序数组的左右两部分
    • 根据中序数组的左边部分的长度,分割前序数组,得到线序数组的左半部分和右边部分
    • 递归给左右字数赋值
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //终止条件1
        if (preorder.length == 0 || inorder.length == 0) {
            return null;
        }
        //创建根节点
        TreeNode root = new TreeNode(preorder[0]);
        //终止条件2
        if (preorder.length == 1) {
            return root;
        }
        //通过中序找到切割点index
        int index = 0 ;
        for (int i = 0; i < inorder.length; i++) {
            if(inorder[i] == root.val) {
                index = i ;
                break;
            }
        }
        //切中序数组  始终左闭右开
        int[] leftInOrderSlice =subArray(inorder,0,index);

        int[] rightInOrderSlice =subArray(inorder,index+1,inorder.length);

        //切前序数组  以中序数组的左半部分的数组长度,切先序数组的左半部分
        int[] leftPreSlice = subArray(preorder,1,leftInOrderSlice.length+1);

        //切后序数组  以中序数组的右半部分的数组长度,切先序数组的右半部分
        int[] rightPreSlice = subArray(preorder,leftPreSlice.length+1,preorder.length);

        //创建左子树
        root.left = buildTree(leftPreSlice,leftInOrderSlice);
        //创建右子树
        root.right = buildTree(rightPreSlice,rightInOrderSlice);
        return root;
    }
    public int[] subArray(int[] array, int start, int end){
        if(start>=end){
            return new int[0];
        }
        int[] result = new int[end-start];
        for (int i = start; i < end; i++) {
            result[i-start] = array[i];
        }
        return result;
    }
}

递归优化

  • 每次都是从原始数组的部分进行递归,可通过开始和结束下标,避免分割数组创建的临时数组,节省空间
class Solution {
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        // 用map保存中序序列的数值对应位置
        for (int i = 0; i < inorder.length; i++) { 
            map.put(inorder[i], i);
        }
        return findNode(preorder, 0, preorder.length, inorder,  0, inorder.length); 
    }

    public TreeNode findNode(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
        // 终止条件
        if (preBegin >= preEnd || inBegin >= inEnd) { 
            return null;
        }
        // 单层循环逻辑:找到前序遍历的第一个元素在中序遍历中的位置
        int rootIndex = map.get(preorder[preBegin]);  
        TreeNode root = new TreeNode(inorder[rootIndex]);
         // 保存中序左子树个数,用来确定前序数列的个数
        int lenOfLeft = rootIndex - inBegin; 
        
        root.left = findNode(preorder, preBegin + 1, preBegin + lenOfLeft + 1,
                inorder, inBegin, rootIndex);
        root.right = findNode(preorder, preBegin + lenOfLeft + 1, preEnd,
                inorder, rootIndex + 1, inEnd);

        return root;
    }
}

总结

  • 递归三部曲:参数和返回值、终止条件、单层循环逻辑
  • 回溯要记录路径,一般用List记录所经过的path,回溯要和递归一起存在
  • 先序+中序,后序+中序遍历的数组,可还原唯一一颗二叉树,先序+后序则无法确认左右顺序,无法还原唯一
  • 还原二叉树,第一步找到根节点,在通过根节点切割中序、先序/后续,要梳理清楚切割的开始和结束位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/634907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PPT里文字太多如何排版-一口气教你7种布局瞬间让PPT高大上起来

简介 这是我们学PPT处于初级到中级进化阶段常做的一件事,就是拿了这种纯文字类版面来做布局。而且这种文字都是政企类的、相当苦涩难懂、无法创意。 因此我们会要求使用7种排版来优化这个版面。这和达芳奇画鸡蛋很像,这样的练习需要坚持一段时间,就是拿了纯文字来beautifu…

【Flutter】如何给按钮添加圆角 自定义圆角按钮样式

文章目录 一、前言二、创建基本按钮三、如何在 Flutter 中创建圆角按钮四、自定义圆角按钮1.修改按钮颜色2.修改圆角半径 五、完整代码示例六、总结 一、前言 欢迎来到 Flutter 的世界&#xff01;在这篇文章中&#xff0c;我们将探索 Flutter 的一些基础知识。但是&#xff0…

【IC设计】Synopsys数字IC设计流程

文章目录 数字IC设计流程前端设计RTL编写和HDL仿真逻辑综合门级仿真形式化验证 后端设计数据准备set mw_phys_refs *set link_library *数据准备 (SDC)数据准备 (RC Techfile) set_tlu_plus_files floor planFloorplan阶段的主要内容&#xff1a;常用命令&#xff1a; placemen…

BiFPN,one-stage,two-stage

目录 BiFPN 语义信息 单stage物体检测算法 双stage skip connection,low-level feature,high level-feature,top-dowm path backbone通常分为几个stage BiFPN BiFPN是双向特征金字塔网络的缩写&#xff0c;是计算机视觉中用于目标检测和分割任务的一种神经网络架构。 …

碳排放预测模型 | Python实现基于传统Holt winter时间序列的碳排放预测模型(预测未来发展趋势)

文章目录 效果一览文章概述研究内容环境准备源码设计学习总结参考资料效果一览 文章概述 碳排放预测模型 | Python实现基于传统Holt winter时间序列的碳排放预测模型(预测未来发展趋势) 研究内容 这是数据集的链接:https://github.com/owid/co2-data/blob/master/owid-co2-d…

一个好的接口自动化测试脚本是怎么写出来的?

目录 前言 1、某个用例的测试目的是什么 2、接口信息的来源 3、一些基本原则 4、断言那些事 5、脚本的后期维护 6、关于测试数据的准备 总结&#xff1a; 前言 谈到接口测试&#xff0c;大家关注更多的是哪个工具更优秀&#xff0c;更好用。但是很少人关注到接口测试用…

Allegor17.2版本WIN11系统CIS配置提示错误解决方案

错误提示&#xff1a; ERROR(ORCIS-6250): Unable to continue. Database access failed. Contact the database administrator to correct the following error(s), and then retry. ODBC Error Code: -1 Description: 在指定的 DSN 中&#xff0c;驱动程序和应用程序之间的体…

架构-系统架构设计模块-2

软件架构风格 #mermaid-svg-daJWV8kQ9nIgH5tZ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-daJWV8kQ9nIgH5tZ .error-icon{fill:#552222;}#mermaid-svg-daJWV8kQ9nIgH5tZ .error-text{fill:#552222;stroke:#55222…

2023年大学计算机专业实习心得14篇

2023年大学计算机专业实习心得精选篇1 20__年已然向我们挥手告别而去了。在20__年初之际&#xff0c;让我们对过去一年的工作做个总结。忙碌的一年里&#xff0c;在领导及各位同事的帮助下&#xff0c;我顺利的完成了20__年的工作。为了今后更好的工作&#xff0c;总结经验&…

【大数据学习篇13】在linux上安装jupyter

下面介绍在liunx怎么安装jupyter&#xff0c;一步到位介绍。 目录 下面介绍在liunx怎么安装jupyter&#xff0c;一步到位介绍。 1、安装Anaconda3​编辑 1.1 自己选择一个位置下载 1.3 配置anaconda的路径 1.3 查看anaconda的版本 2、配置Jupyter Notebook 3、运行Jupy…

Linux UPS配置详解 (山特SANTAK TGBOX-850 )

起因 配置了一台All in One主机&#xff0c;系统是装的PVE&#xff0c;一个linux的虚拟机。里面装了openwrt软路由&#xff0c;还有OMV这个NAS系统。为了防止数据丢失&#xff0c;最好是配置一台UPS来保护数据&#xff0c;毕竟数据无价。于是买了一台山特的TGBOX-850。由于山特…

【群智能算法改进】基于动态折射反向学习和自适应权重的改进麻雀搜索算法[4]【Matlab代码#39】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. SSA算法2. 改进SSA算法2.1 动态折射反向学习策略2.2 自适应权重策略 3. 部分代码展示4. 仿真结果展示5. 资源获取说明 【获取资源请见文章第5节&#xff1a;资源获取】 1. SSA算法 2. 改进SSA算法 2.1 动态折射反向…

局域网内不同网段的设备互相连接设置

目录 介绍1、打开网络连接&#xff0c;找到本地网络->属性->ipv4->属性->高级&#xff1a;2、在高级设置页面&#xff0c;我们添加一个IP&#xff0c;这个IP和板子在一个网段&#xff0c;我这里设置的是192.168.253.101&#xff1a;3、设置完成即可生效&#xff0c…

从0打1 用node express vite搭建一个博客系统系列(完结)

项目使用了Node后端、Express和Vite搭建的全栈博客系统系列&#xff0c;将Vue 2项目重构为Vue 3版本。该系统包含了以下主要模块和功能&#xff1a; 登录和注册功能&#xff1a;用户可以通过注册账号和登录来访问博客系统。 分类列表&#xff1a;展示不同分类的文章&#xff…

编译LeGo-LOAM,并且采用速腾聚创激光雷达与之相连

目录 一、LeGo-LOAM部署二、速腾聚创激光雷达调试三、将速腾聚创激光雷达连到LeGo-LOAM四、解决LeGo-LOAM不保存pcd地图的问题 一、LeGo-LOAM部署 参考链接&#xff1a;实车部署采用速腾聚创RS16激光雷达的LeGo-LOAM LeGO-LOAM初探&#xff1a;原理&#xff0c;安装和测试 1.g…

C语言基础--整型int,长整型long,浮点型double float

本文讲解常见的C语言变量,并举出一些实例 从微软的C语言文档把所有的C语言可定义(就是能用的)截图展示: 还有好几页,不放了,看着都头疼 但是,往往用的最多的,也就是下面的(本篇只讲整数和浮点数) int 整数 整数的定义不用说了吧QAQ int a = 10; //定义一个…

深度学习应用篇-计算机视觉-视频分类[8]:时间偏移模块(TSM)、TimeSformer无卷积视频分类方法、注意力机制

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

本周大新闻|Vision Pro头显重磅发布;苹果收购AR厂商Mira

本周XR大新闻&#xff0c;上周Quest 3发布之后&#xff0c;本周苹果MR头显Vision Pro正式发布&#xff0c;也是本周AR/VR新闻的重头戏。 ​AR方面&#xff0c;苹果发布VST头显Vision Pro&#xff08;虽然本质是台VR&#xff0c;但以AR场景为核心&#xff09;以及visionOS&…

Qt动态调用(外部调用)SDK库(dll动态库)

Qt动态调用SDK库&#xff08;dll动态库&#xff09; ​​​​​​​ 之前的文章&#xff0c;介绍了Qt调用sdk库的方式&#xff0c;大家可以点击查看&#xff08;Q调用SDK库(dll动态库)&#xff09;之前的文章&#xff0c;里面介绍的调用方式就是静态的调用方式。如下图所示&am…

NLP:BIG-bench基准任务的简介、安装、使用方法之详细攻略

NLP&#xff1a;BIG-bench基准任务的简介、安装、使用方法之详细攻略 目录 BIG-bench基准任务的简介 1、BIG-bench基准任务的概述 2、BBL BIG-bench基准任务的安装 BIG-bench基准任务的使用方法 1、使用SeqIO加载BIG-bench json任务的快速启动 BIG-bench基准任务的简介 …