【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一)

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一)

大家好 我是寸铁👊
总结了一篇刷题关于树、dfs、bfs、回溯、递归的文章✨
喜欢的小伙伴可以点点关注 💝


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

模拟分析图

在这里插入图片描述

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTree1(preorder , 0 , preorder.length , inorder , 0 , inorder.length);
    }
    public TreeNode buildTree1(int []preorder , int preLeft, int preRight , int []inorder , int inLeft , int inRight){
        //递归终止条件
        //中序数组中右边界-左边界 < 1
        //返回null
       if(inRight - inLeft < 1){
           return null;
       }
       //只有一个节点
       //则创建该值的节点返回出去即可
       if(inRight - inLeft == 1){
           return  new TreeNode(inorder[inLeft]);
       }

        //前序遍历中的第一个值为根节点的值
        int Val = preorder[preLeft];

        //记录根节点的下标索引
        int rootIdx = 0;

        //在中序数组中查找到第一个值所在的下标
        //用于根据该下标进行数组的切割
        TreeNode root = new TreeNode(Val);
        for(int i = inLeft; i < inRight; i++){
            if(inorder[i] == Val){
                rootIdx = i;
                break;
            }
        }

        //递归根节点的左子树和右子树
        //注意: 编写递归时要统一规范
        //由于上面写的是i < inRight
        //这里需要不断查找每个切分的数组的根节点进行切割。
        //区间是左闭右开的 要统一规范去写
        //清楚是左闭右开后 编写逻辑如下:
        root.left = buildTree1(preorder , preLeft + 1 , preLeft + 1 + (rootIdx - inLeft) , inorder , inLeft , rootIdx);
        root.right = buildTree1(preorder , preLeft+1+(rootIdx - inLeft) , preRight , inorder , rootIdx + 1 , inRight);

        //返回最后的根节点
        //每次递归时,向上返回该节点,接住该节点的是左孩子或者右孩子
        return root;
    }
}




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

模拟分析图

在这里插入图片描述

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        //注:传入的是中序和后序数组的长度
        //区间是左闭右开
        return buildTree1(inorder , 0 , inorder.length , postorder , 0 , postorder.length);
    }
    public TreeNode buildTree1(int []inorder , int inleft, int inRight , int[]postorder , int postLeft,int postRight){
        //对中序数组进行判断
        //如果说中序数组的长度 - 起点下标 < 1 
        //则说明没有元素 返回空
        // 0 - 0 = 0 < 1
         if(inRight - inleft < 1){
            return null;
        }
        //只有一个元素
        //则创建一个该元素的节点返回即可
        if(inRight - inleft == 1){
            return new TreeNode(inorder[inleft]);
        }
        //后序数组中的最后一个元素即为根起点
        int rootVal = postorder[postRight - 1];
        TreeNode root = new TreeNode(rootVal);
       
        int rootIndex = 0;
         //根据拿到的根节点root在中序数组中找到切割点
        for(int i = inleft; i < inRight; i++){
            if(inorder[i] == rootVal){
                rootIndex = i;
            }
        }
        //根据rootIndex在中、后序数组中划分左右子树
        //在中序中划分
        root.left = buildTree1(inorder , inleft , rootIndex, 
                postorder , postLeft , postLeft + (rootIndex - inleft));
        //在后序中划分        
        root.right = buildTree1(inorder, rootIndex + 1, inRight , postorder , postLeft + (rootIndex - inleft) , postRight - 1);        
        return root;
    }
}

112. 路径总和

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        //如果说根节点为空 则无法得到目标和 直接返回false
        if(root == null) return false;
        //采用的是减法 看最后targetSum 减少到最后是否为0
        //递归调用 传入根节点 此时count和为targetSum - 当前根节点的值
        return traversal(root , targetSum - root.val);
    }
    public boolean traversal(TreeNode cur , int count){
        //如果说左子树和右子树都为空(此为叶子节点) 并且count等于0
        //则说明存在路径使得节点之和为targetSum
        //返回true
        if(cur.left == null && cur.right == null && count == 0)return true;
        //否则返回false
        if(cur.left == null && cur.right == null && count == 0)return false;
        //递归逻辑
        //递归左子树
        if(cur.left != null){
            //减去当前遍历到的节点值
            count -= cur.left.val;
            //注意:这里需要向上返回布尔值
            //如果往左子树遍历的结果为true
            //则向上返回true
            if(traversal(cur.left , count)){
                return true;
            }
            //回溯 把之前减去的节点值加上
            //再从另一个分支去寻找是否存在路径
            count += cur.left.val;
        }
        //同理,递归右子树
        if(cur.right != null){
            count -= cur.right.val;
            if(traversal(cur.right , count)){
                return true;
            }
            count += cur.right.val;
        }
        return false;
    }
}



113. 路径总和 II

相比较 112. 路径总和
113. 路径总和 II || 与下面的 129. 求根节点到叶节点数字之和
共同的逻辑都是需要遍历一棵树从根节点到所有叶子节点
这意味着需要一个数据结构(list)去存储所有经过的路径上的节点
也就意味着不需要返回值,但是由于需要遍历所有的叶子节点
这里需要进行向上回溯,也就是怎么来的就怎么去(恢复现场)

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //result队列用于接收满足条件的path
    List<List<Integer>> result;
    //path用于接收每次搜索的结果
    //这里不用开启全局变量
    //原因:path会遍历到叶子节点会向上回溯 
    LinkedList<Integer> path;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
      result = new LinkedList<>();
      path = new LinkedList<>();
      travesal(root , targetSum);
      return result;
    }
    //这里由于有path接收搜索的结点
    //所以,这里不需要去返回值
    public void travesal(TreeNode root , int count){
        //如果说根节点为空 则直接结束
        if(root == null) return;
        //先把当前的节点值加入到path队列中
        path.offer(root.val);
        //然后,更新当前的count 把当前添加入队列的节点值减去
        count -= root.val;
        //接着,处理临界条件,也就是遍历到叶子节点对答案的判断
        if(root.left == null && root.right == null && count == 0){
            //满足条件则把当前遍历的节点添加到path队列中
            result.add(new LinkedList<>(path));
        }
        //向下递归,遍历左子树和右子树
        //这里是直接往左子树或者右子树的某个方向能走的路走到底
        //无论是往右还是左走 走到底即可
        //走到底无路可走后再向上回溯 依次移除最后一个元素 再去搜索其他分支
        travesal(root.left , count);
        travesal(root.right , count);
        path.removeLast();
    }
}

debug

class Solution {
    List<List<Integer>> result;
    LinkedList <Integer> path;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        result = new LinkedList<>();
        path = new LinkedList<>();
        travesal(root , targetSum);
        return result;
    }
    public void travesal(TreeNode root , int count){
        if(root == null)return;
        path.offer(root.val);
        count -= root.val;
          System.out.println("111111111");
         System.out.println(path);
        if(root.left == null && root.right == null && count == 0){
            //打印出来去看path的变化过程
             System.out.println("22222222");
            System.out.println(path);
            result.add(new LinkedList<>(path));
        }
        travesal(root.left , count);
        System.out.println("leftleftleftleftleftleft");
        System.out.println(path);
        travesal(root.right , count);
        System.out.println("333333333333");
        System.out.println(path);
        
        //依次移除掉最后一个节点,向上回溯
        //直至移除到最后一个根节点
        path.removeLast();
    }
}

129. 求根节点到叶节点数字之和

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //path存储dfs到的节点
    List<Integer>path = new LinkedList<>();
   //记录最终求和的结果
    int res = 0;
    public int sumNumbers(TreeNode root) {
        //如果root为null 则返回0
        if(root == null)return 0;
        //如果root不为null 则把根节点添加到path中
        path.add(root.val);
        travesal(root);
        return res;
    }
    public void travesal(TreeNode root){
        //遍历到叶子节点则对当前的path的值求和
        if(root.left == null && root.right == null){
            res += listToInt(path);
        }
        //遍历左子树
        if(root.left != null){
            //先添加左子树节点的值
            path.add(root.left.val);
            //再继续递归到下一层
            travesal(root.left);
            //移除掉当前队列中的最后一个元素 向上回溯
            path.remove(path.size() - 1);
        }
        //遍历右子树
        if(root.right != null){
            path.add(root.right.val);
            travesal(root.right);
            path.remove(path.size() - 1);
        }
    }
    //对path中存储的节点值进行求和
    public int listToInt(List<Integer> path){
        int sum = 0;
        //这里由于list是队列 先进先出
        //在原来的sum基础上乘10 再加上最后一个元素即可
        for(Integer s : path){
            sum = sum * 10 + s;
        }
        return sum;
    }
    
}

总结

大逻辑其实还是最核心的三个点,一个是根节点,一个是左孩子 ,一个是右孩子
可以把递归函数看成是一个整体部分,整体的去对左子树进行处理,整体
的去对右子树进行处理,然后返回结果或者说记录结果,不必去深究递归里面的细节,会让整个的解题思路变得十分复制混乱,就是理解为递归函数去帮助你进行处理,最后返回一个结果或者将结果存起来就好了!


看到这里的小伙伴,恭喜你又掌握了一个技能👊
希望大家能取得胜利,坚持就是胜利💪
我是寸铁!我们下期再见💕


往期好文💕

保姆级教程

【保姆级教程】Windows11下go-zero的etcd安装与初步使用

【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero

【Go-Zero】手把手带你在goland中创建api文件并设置高亮


报错解决

【Go-Zero】Error: user.api 27:9 syntax error: expected ‘:‘ | ‘IDENT‘ | ‘INT‘, got ‘(‘ 报错解决方案及api路由注意事项

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案

【Go-Zero】【error】 failed to initialize database, got error Error 1045 (28000):报错解决方案

【Go-Zero】Error 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)报错解决方案

【Go-Zero】type mismatch for field “Auth.AccessSecret“, expect “string“, actual “number“报错解决方案

【Go-Zero】Error: user.api 30:2 syntax error: expected ‘)‘ | ‘KEY‘, got ‘IDENT‘报错解决方案

【Go-Zero】Windows启动rpc服务报错panic:context deadline exceeded解决方案


Go面试向

【Go面试向】defer与time.sleep初探

【Go面试向】defer与return的执行顺序初探

【Go面试向】Go程序的执行顺序

【Go面试向】rune和byte类型的认识与使用

【Go面试向】实现map稳定的有序遍历的方式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows系统中定时执行python脚本

背景&#xff1a;本地Windows系统指定目录下会有文件的修改新增&#xff0c;这些变化的文件需要定时的被上传到git仓库中&#xff0c;这样不需要每次变更手动上传了。 首先编写一个检测文件夹下文件变化并且上传git仓库的python脚本(确保你已经在E:\edc_workspace\data_edc_et…

uniapp-提现功能(demo)

页面布局 提现页面 有一个输入框 一个提现按钮 一段提现全部的文字 首先用v-model 和data内的数据双向绑定 输入框逻辑分析 输入框的逻辑 为了符合日常输出 所以要对输入框加一些条件限制 因为是提现 所以对输入的字符做筛选,只允许出现小数点和数字 这里用正则实现的 小数点…

力扣面试经典150 —— 1-5题

力扣面试经典150题在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题&#xff0c;安装 Debug LeetCode 插件可以免费 debug本文使用 python 语言解题&#xff0c;文中 “数组” 通常指 python 列表&#xff1b;文中 “指针” 通常指 python 列表索引 文章目录 1. [简单] 合并…

TongWEB(东方通),部署WEB前后端项目步骤

我的系统: 银河麒麟桌面系统V10(SP1)(兆芯) 环境需要搭建好,什么redis,数据库等 1.准备项目前端war包 (我后端项目本就是war部署,jar转war自行百度一下吧) 进入前端打包好的dist文件夹,创建一个文件夹 WEB-INF ,再在 WEB-INF 里创建一个 web.xml 文件,文件内容: <web-…

谁说常量字符串不可修改

哈喽&#xff0c;我是子牙&#xff0c;一个很卷的硬核男人 深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果&#xff0c;一年磨一剑&#xff0c;三年先后做了这些课程&#xff1a;手写JVM、手写OS、…

接口性能优化的小技巧

目录 1.索引 1.1 没加索引 1.2 索引没生效 1.3 选错索引 2. sql优化 3. 远程调用 3.1 并行调用 3.2 数据异构 4. 重复调用 4.1 循环查数据库 4.2 死循环 4.3 无限递归 5. 异步处理 5.1 线程池 5.2 mq 6. 避免大事务 7. 锁粒度 7.1 synchronized 7.2 redis分…

git 使用总结

文章目录 git merge 和 git rebasegit mergegit rebase总结 git merge 和 git rebase git merge git merge 最终效果说明&#xff1a; 假设有一个仓库情况如下&#xff0c;现需要进行 merge&#xff1a; merge 操作流程&#xff1a; merge 的回退操作&#xff1a; git reba…

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

常见的芯片行业ERP:SAP Business One ERP系统

在现代企业管理中&#xff0c;企业资源规划(ERP)系统已成为不可或缺的工具。特别是在高度复杂和竞争激烈的芯片行业中&#xff0c;一款高效、全面的ERP系统更是助力企业实现精细管理、提升竞争力的关键。SAP Business One ERP系统便是其中一款备受推崇的选择。 SAP Business On…

2023 龙蜥操作系统大会演讲实录:《兼容龙蜥的云原生大模型数据计算系统——πDataCS》

本文主要分三部分内容&#xff1a;第一部分介绍拓数派公司&#xff0c;第二部分介绍 πDataCS 产品&#xff0c;最后介绍 πDataCS 与龙蜥在生态上的合作。 杭州拓数派科技发展有限公司&#xff08;简称“拓数派”&#xff0c;英文名称“OpenPie”&#xff09;是国内基础数据计…

alist修改密码(docker版)

rootarmbian:~# docker exec -it [docker名称] ./alist admin set abcd123456 INFO[2024-02-20 11:06:29] reading config file: data/config.json INFO[2024-02-20 11:06:29] load config from env with prefix: ALIST_ INFO[2024-02-20 11:06:29] init logrus..…

bilibili尚硅谷周阳老师JUC并发编程与源码分析课程笔记第十一章——Synchronized与锁升级

文章目录 先从阿里及其它大厂面试题说起本章路线总纲阿里手册对锁使用的强制要求Synchronized锁优化的背景Synchronized锁的升级过程Synchronized锁的升级标志 Synchronized的性能变化Java5以前&#xff0c;只有Synchronized&#xff0c;这个是操作系统级别的重量级锁为什么每一…

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁

PublishFolderCleaner – Github 测试环境: .Net 8 Program.cs 代码 // https://github.com/dotnet-campus/dotnetcampus.DotNETBuildSDK/tree/master/PublishFolderCleanerusing System.Diagnostics; using System.Text;// 名称, 不用写 .exe var exeName "AbpDemo&…

【数学建模竞赛考点】近五年数维杯数学建模题型及算法模型总结

20204年第九届数维杯数学建模竞赛在5月10号开赛&#xff0c;为了帮助小伙伴们赛前充分准备&#xff0c;并且快速掌握历年的赛题类型&#xff0c;在这里给大家整理出了近五年的数维杯数学建模竞赛题目及考点方向&#xff0c;便于小伙伴们更好的巩固学习。 2019年 A题&#xff…

当项目经理的一定要考PMP嘛?

PMP资格认证并不是强制性要求&#xff0c;但强烈建议考虑获取该资格&#xff01;首先让我们来了解一下PMP是什么&#xff0c;然后再谈谈为什么建议考取PMP资格的理由。 PMP&#xff08;Project Management Professional&#xff09;是项目管理专业人员的资格认证。该认证由全球…

落雪音乐换源失败播放不了音乐——保姆级解决方法

不想看原因可以直接跳转到下面的解决方法 一、换源失败的原因二、解决方法注意&#xff01;2.1电脑版解决方法2.2 手机版解决方法前提&#xff08;必看&#xff01;&#xff09;解决方法 一、换源失败的原因 落雪开发者原话&#xff1a;虽然我们之前做了一些努力&#xff08;如…

《剑指Offer》笔记题解思路技巧优化_Part_7

《剑指Offer》笔记&题解&思路&技巧&优化_Part_7 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相识&#x1f622;&#x1f622;&#x1f622; 开始刷题&#x1f7e2;1. LCR 179. 查找总价格为目标值的两个商品——和…

ocr识别tesseract.js本地复现

来源&#xff1a; https://github.com/naptha/tesseract.js chatgpt今天帮倒忙&#xff0c;一直给一些旧的东西&#xff0c;代码就老报错&#xff0c;最后还是我出面看看log和err调了一下&#xff0c;还的是我啊 复现效果 这个挺好复现的&#xff0c;用的英文模式比中文识别…

Matlab/simulink光伏发电的扰动观察法MPPT仿真(持续更新)

1.光伏发电的电导增量法MPPT仿真 2.光伏发电的恒定电压法MPPT仿真 3.光伏发电的扰动观察法MPPT仿真 4.光伏发电的占空比法MPPT仿真 5.基于神经网络的MPPT光伏发电仿真 6. 基于模糊控制的MPPT光伏发电仿真 7. 基于粒子群算法&#xff08;PSO&#xff09;的500w光伏系统MPPT控…

如何使用Douglas-042为威胁搜索和事件应急响应提速

关于Douglas-042 Douglas-042是一款功能强大的PowerShell脚本&#xff0c;该脚本可以提升数据分类的速度&#xff0c;并辅助广大研究人员迅速从取证数据中筛选和提取出关键数据。 该工具能够搜索和识别Windows生态系统中潜在的安全漏洞&#xff0c;Douglas-042会将注意力放在…