news 2026/4/16 9:02:37

代码随想录算法第五十天| KamaCoder98所有可达路径、LeetCode797所有可能的路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码随想录算法第五十天| KamaCoder98所有可达路径、LeetCode797所有可能的路径

KamaCoder 98 所有可达路径

题目链接:98.所有可达路径

文档讲解:图论理论基础 | 图论深度优先搜索理论 | 代码随想录

视频讲解:图论理论基础 | 图论深度优先搜索理论 | 所有可达路径

思路与感想:这是我接触图论的第一题,用到了图论中的深搜进行解决,卡哥说这是深搜的一道裸题,我从中知道了图论深搜的模板其实就是之前回溯算法的模板,所以上手起来还是比较容易的。KamaCoder上面也做了一些题目了,对于ACM代码模式也有些熟悉起来了,听说图论上大部分题目都要在KamaCoder上做了,我也挺开心正好再强化一下ACM代码模式,到时候面试的时候也不至于写的乱七八糟。依旧三部曲,第一步确定深搜函数的参数,一般就graph,当前遍历的节点x,path的终点n。这个graph一般在力扣的核心代码模式里面会直接给出,但是ACM模式就要自己去创建这个graph。卡哥介绍了三种图的构造方法,第一种是朴素存储,就是创建一个n * 2的数组,[[起点1,连接点1],[起点1,连接点2]........],当然也可以采用Map的key和value来记录这个连接,这种方法的优点在于比较灵活,缺点也很明显就是当你要取到某个起点的连接方式的时候你需要遍历这个数组,就是On的时间复杂度。第二种方法就是邻接矩阵,创建一个n * n的二维数组,graph[起点1][连接点1]、graph[起点3][连接点2],这样通过两个下标值就可以确定从某个点指向某个点的连接了,值得话可以设置为权值,如果没权值光有连接就设置为1,至于0就说明两个点之间没有连接。这个方法的缺点就在于空间复杂度为On2,而且很容易造成空间浪费,极端地来讲如果n很大,但是呢里面只有两个点之间存在连接,相当于整个数组可能只有那个位置值不为0,其它都是0了。第三种方法就是邻接表,可以想象成左边由一个长度为n的纵向一维数组,每个位置右边都有一个长度不定的链表,链表存储的是左边数组里面的所有可以连接的点,个人感觉很想哈希表处理哈希冲突时拉链法形成的结构。这个优点就是左边一维数组空间虽定,但右边链表可以根据实际情况分配容量,不至于没有连接却分配空间浪费了。总结一下适用情形,如果图中边比较少点比较多的情况就可以用邻接表,如果点和边都很多就可以用邻接矩阵。一般来说都推荐邻接矩阵,因为邻接表既有数组又有链表创建起来会比较麻烦。接下来讲讲在ACM模式下解决这道题目需要注意的点。首先就是导包操作,记得导入util下的List和ArrayList,这俩不能直接用。深搜函数里面的for循环操作其实就是遍历以当前遍历的点x的所有可能连接点,而这一标志就是graph[x][i]等于1,就说明有这个连接。最后输出result的时候记得每个路径的最后一个数输出的时候后面不能带空格所有要特殊打印。

收获:1.重温ACM代码模式;2.图论中的深搜模板;3.图论深搜过程;4.图论创建的三种方法

import java.util.Scanner; import java.util.List; import java.util.ArrayList; public class Main { static List<List<Integer>> result = new ArrayList<>(); // 记录所有有效path static List<Integer> path = new ArrayList<>(); // 记录每一个path public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // 接收N个节点数 int m = sc.nextInt(); // 接收M条连接 int[][] graph = new int[n + 1][n + 1]; // 创建邻接矩阵 for (int i = 0; i < m; i++) { int s = sc.nextInt(); int t = sc.nextInt(); graph[s][t] = 1; // 邻接矩阵初始化,有连接就初始化为1 } path.add(1); // 开启深搜 dfs(graph, 1, n); if (result.isEmpty()) { // 如果搜出来结果为空说明没有路径能够从1到n System.out.println(-1); } for (List<Integer> pa : result) { // 遍历所有可能的path for (int i = 0; i < pa.size() - 1; i++) { // 把path中的元素一个个输出 System.out.print(pa.get(i) + " "); } System.out.println(pa.get(pa.size() - 1)); // 最后一个元素特殊处理,尾部不留空格 } } public static void dfs(int[][] graph, int x, int n) { if (x == n) { // 当前遍历的节点正好是path终点,收获结果 result.add(new ArrayList<>(path)); return; } for (int i = 1; i <= n; i++) { // 以当前遍历节点x为基准,遍历它的所有连接 if (graph[x][i] == 1) { // 必须要有连接 path.add(i); // 添加与它连接的元素到path中 dfs(graph, i, n); // 往下深搜递归 path.remove(path.size() - 1); // 回溯撤销上一次移动 } } } }

LeetCode 797 所有可能的路径

题目链接:797.所有可能的路径

思路与感想:本题是力扣上的,采用的核心代码模式,所以无需进行图的创建,题目直接在方法中给了graph。这一题跟上一题的区别在于这个终点需要自己求,然后graph的含义优点不一样,核心思路其实都是一样的。

收获:1.不同含义的graph如何体现在深搜中

// DFS深搜 class Solution { List<Integer> path = new ArrayList<>(); // 存储每一个有效路径 List<List<Integer>> result = new ArrayList<>(); // 存储所有有效路径 public List<List<Integer>> allPathsSourceTarget(int[][] graph) { int end = 0; // path的终点 for (int i = 0; i < graph.length; i++) { if (graph[i].length == 0) { continue; } end = Math.max(end,graph[i][graph[i].length - 1]); // 是graph中的最大值 } path.add(0); // 每一个path都是从起点0开始的 dfs(graph, 0, end); // 开启深搜,0作为起点,graph[graph.length - 2][0] + 1作为n个节点 return result; } void dfs(int[][] graph, int x, int end) { if (x == end) { // 当前遍历到的x等于终点值n - 1,注意n是节点个数,n - 1才是终点值 result.add(new ArrayList<>(path)); // 收获有效路径 return; } for (int i = 0; i < graph[x].length; i++) { // 遍历当前遍历到的x点在graph中连接的点 path.add(graph[x][i]); // 添加进path中 dfs(graph, graph[x][i], end); // 进入下一层递归 path.remove(path.size() - 1); // 回溯撤销最近一次移动 } } }

今天算法花了大概五个小时,主要看了图论一些入门概念还有深搜广搜思想,写了两道深搜的题目。早上实验课的爬虫半个小时代码就跑通了之后就一直听歌看并发那块的八股,感觉还是很有收获的。把线程进程还有并发并行异步这些小概念重温了一下,以及线程的六种状态,用户态和内核态两种CPU特权级别的区别,虚拟线程和普通线程之间的区别,以及乐观锁和悲观锁以及基于乐观锁的无锁并发,以及其中的CAS算法,虽然好多地方都没看懂,不过JUC这块既然是重点就先做一个大致了解后面再照着源码边理解边背。下午感觉状态不好而且犯困不想去健身,结果一下课还是跑到健身房去了,下午把务虚笔记看完了,没怎么看懂,下一本拜读毕飞宇老师的推拿。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 9:55:27

STM32开发者必看:Keil安装避坑指南

STM32开发者必看&#xff1a;Keil安装避坑指南 在嵌入式开发的世界里&#xff0c;如果你正准备点亮第一颗LED、调试第一个GPIO&#xff0c;或是跑通一段ADC采样代码——那么恭喜你&#xff0c;已经迈出了成为STM32工程师的第一步。但在这之前&#xff0c;有一个绕不开的“入门仪…

作者头像 李华
网站建设 2026/4/12 18:21:38

STM32中SMBus通信配置:手把手教程(从零实现)

STM32中SMBus通信实战&#xff1a;从协议到代码的完整实现你有没有遇到过这样的场景&#xff1f;系统里接了几个温度传感器和电源监控芯片&#xff0c;IC总线上时不时就“卡死”——主控发不出数据、读不到回应&#xff0c;最后只能靠复位解决。调试时用逻辑分析仪一看&#xf…

作者头像 李华
网站建设 2026/4/15 6:03:43

开源社区力荐:GPT-SoVITS成为GitHub热门语音项目的原因

GPT-SoVITS&#xff1a;为何这款开源语音克隆项目在GitHub上迅速走红&#xff1f; 在内容创作、虚拟主播和无障碍交互日益普及的今天&#xff0c;用户不再满足于机械感十足的合成语音。他们想要的是——用自己或特定人物的声音&#xff0c;自然流畅地说出任何想说的话。而这一需…

作者头像 李华
网站建设 2026/4/1 19:32:25

跨语言语音合成实现路径:GPT-SoVITS支持中英混读场景

跨语言语音合成实现路径&#xff1a;GPT-SoVITS支持中英混读场景 在智能语音助手、有声内容创作和虚拟角色交互日益普及的今天&#xff0c;用户对语音合成系统的要求早已超越“能说话”这一基本功能。人们期待的是自然、个性、多语种无缝切换的声音体验——尤其是在中文为主但频…

作者头像 李华
网站建设 2026/4/16 8:31:16

10、天气信息模块开发全解析

天气信息模块开发全解析 1. 天气信息函数的编写 在开发过程中,若一切顺利, $weather 对象会被返回以供使用。此时,我们需要编写调用此函数的代码。在 weather_info.inc 文件里,还需编写一个名为 weather_info_temp() 的函数,它将返回带有度数符号和测量单位的温度。…

作者头像 李华