代码随想录算法训练营第五十二天(图论)| 98. 所有可达路径、深度优先搜索、广度优先搜索

邻接矩阵

邻接矩阵是一种使用二维数组来表示图的方法。矩阵中的元素表示节点之间是否存在边。如果存在边,则对应的矩阵元素为1(或边的权重);否则为0。

特点:

  • 空间复杂度高:无论图是否稀疏,邻接矩阵都需要O(V^2)的空间,因为每个节点都需要为所有其他节点预留位置。
  • 查找效率高:查找任意两节点之间是否存在边非常高效,只需要访问一次数组,时间复杂度为O(1)。
  • 不适合稀疏图:对于边远少于顶点平方的稀疏图,邻接矩阵会浪费大量空间。
        int[][] adjMatrix = new int[n + 1][n + 1];for (int i = 0; i < m; i++) {int u = scanner.nextInt();int v = scanner.nextInt();adjMatrix[u][v] = 1; // 对于有向图,如果是无向图,还需要设置 adjMatrix[v][u] = 1;}

邻接表

邻接表是一种通过链表或数组来表示图的方法。对于图中的每个节点,邻接表存储一个链表,链表中的元素是该节点的所有邻居节点。

特点:

  • 空间复杂度低:对于稀疏图(边的数量远小于顶点的平方),邻接表比邻接矩阵节省空间。它的空间复杂度是O(V + E),其中V是顶点数,E是边数。
  • 查找效率高:查找某个节点的所有邻居节点非常高效,只需要遍历链表即可。
  • 添加边高效:向图中添加一条边只需要在对应的链表中添加一个节点,时间复杂度为O(1)。
        // 初始化邻接表List<List<Integer>> adjList = new ArrayList<>();for (int i = 0; i <= n; i++) {adjList.add(new ArrayList<>());}for (int i = 0; i < m; i++) {int u = scanner.nextInt();int v = scanner.nextInt();adjList.get(u).add(v); // 对于有向图,如果是无向图,还需要 adjList.get(v).add(u);}

98. 所有可达路径

在这里插入图片描述
题解:

public class AllReachablePaths {/*** 方法一:使用邻接表表示图,深度优先搜索遍历图,寻找所有从节点1到节点n的路径** @param graph 图的邻接表表示* @param n     节点总数* @return 所有从节点1到节点n的路径列表*/public static List<List<Integer>> allPathsSourceTarget1(List<LinkedList<Integer>> graph, int n) {List<List<Integer>> result = new ArrayList<>();LinkedList<Integer> path = new LinkedList<>();path.add(1); // 从节点1开始dfs1(graph, 1, n, path, result); // 深度优先搜索return result;}/*** 深度优先搜索遍历图的递归函数** @param graph  图的邻接表表示* @param x      当前遍历到的节点* @param n      终点节点* @param path   当前路径* @param result 存储所有路径的结果集合*/private static void dfs1(List<LinkedList<Integer>> graph, int x, int n, LinkedList<Integer> path, List<List<Integer>> result) {if (x == n) { // 找到一条从1到n的路径result.add(new ArrayList<>(path)); // 将当前路径加入结果集合return;}for (int i : graph.get(x)) { // 遍历节点x的邻居节点path.add(i); // 将邻居节点i加入路径中dfs1(graph, i, n, path, result); // 递归遍历从节点i出发的路径path.removeLast(); // 回溯,移除最后一个节点,尝试其他路径}}/*** 方法二:使用邻接矩阵表示图,深度优先搜索遍历图,寻找所有从节点1到节点n的路径** @param graph 图的邻接矩阵表示* @param n     节点总数* @return 所有从节点1到节点n的路径列表*/public static List<List<Integer>> allPathsSourceTarget2(int[][] graph, int n) {List<List<Integer>> result = new ArrayList<>();LinkedList<Integer> path = new LinkedList<>();path.add(1); // 从节点1开始dfs2(graph, 1, n, path, result); // 深度优先搜索return result;}/*** 深度优先搜索遍历图的递归函数** @param graph  图的邻接矩阵表示* @param x      当前遍历到的节点* @param n      终点节点* @param path   当前路径* @param result 存储所有路径的结果集合*/private static void dfs2(int[][] graph, int x, int n, LinkedList<Integer> path, List<List<Integer>> result) {if (x == n) { // 找到一条从1到n的路径result.add(new ArrayList<>(path)); // 将当前路径加入结果集合return;}for (int i = 1; i <= n; i++) {if (graph[x][i] == 1) { // 如果节点x与节点i相连path.add(i); // 将节点i加入路径dfs2(graph, i, n, path, result); // 递归遍历从节点i出发的路径path.removeLast(); // 回溯,移除最后一个节点,尝试其他路径}}}/*** 主函数,用于测试两种方法*/public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt(); // 节点总数int m = scanner.nextInt(); // 边数// 使用邻接表表示图,节点编号从1到n,因此需要n+1大小的数组List<LinkedList<Integer>> graph1 = new ArrayList<>(n + 1);for (int i = 0; i <= n; i++) {graph1.add(new LinkedList<>()); // 初始化邻接表}// 使用邻接矩阵表示图,节点编号从1到n,因此需要n+1大小的数组int[][] graph2 = new int[n + 1][n + 1];// 读取每条边的起点和终点,构建邻接表和邻接矩阵for (int i = 0; i < m; i++) {int s = scanner.nextInt(); // 边的起点int t = scanner.nextInt(); // 边的终点graph1.get(s).add(t); // 邻接表:s -> t,无向图graph2[s][t] = 1; // 邻接矩阵:s -> t,无向图}// 使用方法一寻找所有路径List<List<Integer>> paths1 = allPathsSourceTarget1(graph1, n);System.out.println("Method 1 - Using Adjacency List:");printPaths(paths1);// 使用方法二寻找所有路径List<List<Integer>> paths2 = allPathsSourceTarget2(graph2, n);System.out.println("Method 2 - Using Adjacency Matrix:");printPaths(paths2);}/*** 输出所有路径的辅助函数** @param paths 路径列表*/private static void printPaths(List<List<Integer>> paths) {if (paths.isEmpty()) {System.out.println(-1); // 如果没有找到路径,输出-1} else {for (List<Integer> path : paths) {for (int i = 0; i < path.size() - 1; i++) {System.out.print(path.get(i) + " "); // 输出路径中的节点}System.out.println(path.get(path.size() - 1)); // 输出路径的最后一个节点}}}
}

深度优先搜索理论基础

深度优先搜索(DFS, Depth-First Search)可以形象地理解为“碰到南墙再回头”。具体来说,DFS 的操作方式如下:

从起始节点开始,沿着一条路径一直往前走,尽可能深地进入到图的最深处。
当走到无法继续前进(即碰到“南墙”)的时候,回溯到上一个节点,寻找其他未访问的路径。
重复上述步骤,直到所有节点都被访问过。
这种搜索方式类似于在迷宫中行走,尽量往前走,直到无法前进时再回头,寻找其他可能的路径。

dfs模板:

void dfs(参数) {if (终止条件) {存放结果;return;}for (选择:本节点所连接的其他节点) {处理节点;dfs(图,选择的节点); // 递归回溯,撤销处理结果}
}

广度优先搜索理论基础

广度优先搜索(BFS, Breadth-First Search)的基本原理是逐层地搜索图或树的节点,从起始节点开始,首先访问所有与起始节点直接相连的节点(即第一层节点),然后逐层向外扩展,直到找到目标节点或所有可达节点都被访问过为止。

bfs需要一个容器,能保存我们要遍历过的元素就可以,那么用队列,还是用栈,甚至用数组,都是可以的。

用队列的话,就是保证每一圈都是一个方向去转,例如统一顺时针或者逆时针。
因为队列是先进先出,加入元素和弹出元素的顺序是没有改变的。

如果用栈的话,就是第一圈顺时针遍历,第二圈逆时针遍历,第三圈有顺时针遍历。
因为栈是先进后出,加入元素和弹出元素的顺序改变了。

bf’s模版:

    // BFS 函数模板void bfs(int start, List<List<Integer>> graph) {// 图的顶点数int V = graph.size();// 记录节点是否已访问boolean[] visited = new boolean[V];// 创建队列用于 BFSQueue<Integer> queue = new LinkedList<>();// 初始化,将起始节点加入队列并标记为已访问queue.offer(start);visited[start] = true;// BFS 主循环while (!queue.isEmpty()) {// 出队列int v = queue.poll();// 处理当前节点 v,可以是打印节点值或其他操作System.out.print(v + " ");// 遍历当前节点 v 的所有相邻节点for (int next : graph.get(v)) {// 如果相邻节点未被访问过,则加入队列并标记为已访问if (!visited[next]) {queue.offer(next);visited[next] = true;}}}}

使用场景对比

特点深度优先搜索 (DFS)广度优先搜索 (BFS)
主要应用路径查找、连通分量、拓扑排序、回溯算法等最短路径查找、层次遍历、二分图检测等
内存占用较少,因为只需要存储当前路径和少量节点较多,因为需要存储当前层的所有节点
实现复杂度相对简单,可以使用递归或显式栈实现较复杂,需要使用队列实现
路径长度不一定是最短路径总是最短路径
遍历顺序深入到尽可能深的节点,然后回溯按层次遍历,从起点开始逐层扩展
适用场景迷宫问题、连通分量检测、回溯问题、拓扑排序等无权图的最短路径查找、层次遍历、二分图检测等

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

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

相关文章

前端Canvas入门——一些注意事项

创建渐变的三种方法&#xff1a; createLinearGradient() - 线性渐变 createRadialGradient() - 径向渐变&#xff08;放射性渐变&#xff09; createConicGradient() - 锥形渐变 这三种的核心观点都是&#xff1a; 创建一个gradient对象&#xff0c;然后调用addColorStop()方法…

【java】力扣 合并两个有序链表

文章目录 题目描述题目链接思路代码 题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 题目链接 21. 合并两个有序链表 思路 先定义一个哨兵节点dummy&#xff0c;为了方便解题 然后定义一个节点pre&#xff0…

paddlepaddle2.6,paddleorc2.8,cuda12,cudnn,nccl,python10环境

1.安装英伟达显卡驱动 首先需要到NAVIDIA官网去查自己的电脑是不是支持GPU运算。 网址是&#xff1a;CUDA GPUs | NVIDIA Developer。打开后的界面大致如下&#xff0c;只要里边有对应的型号就可以用GPU运算&#xff0c;并且每一款设备都列出来相关的计算能力&#xff08;Compu…

为二进制文件添加.gnu_debugdata调试信息

前言 在使用gcc/g编译二进制文件过程中&#xff0c;如果添加了-g参数&#xff0c;编译出来的二进制文件会带有debug信息&#xff0c;供调试使用。但是debug信息往往占用空间很大&#xff0c;导致二进制文件太大&#xff0c;在发布到生产环境时&#xff0c;一般会去掉调试信息&…

(南京观海微电子)——电容应用及选取

什么是电容器&#xff1f; 电容器是一种在内部电场中储存能量的电子器件。它与电阻器、电感器一样&#xff0c;都是基本的无源电子元件。所有电容器都具有相同的基本结构&#xff0c;两块导电极板中间由绝缘体隔开&#xff0c;该绝缘体称为电介质&#xff0c;可在施加电场后发…

时间轮算法理解、Kafka实现

概述 TimingWheel&#xff0c;时间轮&#xff0c;简单理解就是一种用来存储若干个定时任务的环状队列&#xff08;或数组&#xff09;&#xff0c;工作原理和钟表的表盘类似。 关于环形队列&#xff0c;请参考环形队列。 时间轮由两个部分组成&#xff0c;一个环状数组&…

一文了解MySQL的表级锁

文章目录 ☃️概述☃️表级锁❄️❄️介绍❄️❄️表锁❄️❄️元数据锁❄️❄️意向锁⛷️⛷️⛷️ 介绍 ☃️概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0…

Coze:如何使用团队空间?

你好&#xff0c;我是三桥君 团队空间&#xff0c;是一个允许我们组建团队并共享机器人、插件等资源的功能。 好的&#xff0c;让我们开始创建一个团队。我们将这个团队命名为“三桥君AI”&#xff0c;并在描述中也填写“这里是关于“三桥君AI”团队的描述”。点击确定后&…

VMware_centos8安装

目录 VMware Workstation Pro的安装 安装centos VMware Workstation Pro的安装 正版VMware 17百度网盘下载链接 (含秘钥) 链接&#xff1a;https://pan.baidu.com/s/16zB-7IAACM_1hwR1nsk12g?pwd1111 提取码&#xff1a;1111 第一次运行会要求输入秘钥 秘钥在上边的百度网盘…

【CUDA|CUDNN】安装

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 显卡驱动安装参考之前的文章 cuda、cudnn 安装 1. cuda 安装 访问https://developer.nvidia.com/cuda-toolkit-archive 选择需要的版本&#xff1a;h…

Selenium使用注意事项:

find_element 和 find_elements 的区别 WebDriver和WebElement的区别 问题&#xff1a; 会遇到报错&#xff1a; selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector",&…

双管正激小功率电源的设计与实现

正激变换由于拓扑简单&#xff0c; 升/ 降压范围宽&#xff0c; 广泛应用于中小功率电源变换场合。正激变换器的输出功率不象反激变换器那样受变压器储能的限制&#xff0c; 因此输出功率较反激变换器大&#xff0c; 但是正激变换器的开关管电压应力高&#xff0c; 为两倍输入电…

视频监控汇聚平台:通过SDK接入大华DSS视频监控平台的源代码解释和分享

目录 一、视频监控汇聚平台 1、概述 2、视频接入能力 3、视频汇聚能力 二、大华DSS平台 1、DSS平台概述 2、大华DSS平台的主要特点 &#xff08;1&#xff09;高可用性 &#xff08;2&#xff09;高可靠性 &#xff08;3&#xff09;易维护性 &#xff08;4&#xf…

【Mongodb-04】Mongodb聚合管道操作基本功能

Mongodb系列整体栏目 内容链接地址【一】Mongodb亿级数据性能测试和压测https://zhenghuisheng.blog.csdn.net/article/details/139505973【二】springboot整合Mongodb(详解)https://zhenghuisheng.blog.csdn.net/article/details/139704356【三】亿级数据从mysql迁移到mongodb…

SpringCloud第三篇(服务中心与OpenFeign)

p 文章目录 一、服务中心二、Nacos注册中心 一、服务中心 在上一章我们实现了微服务拆分&#xff0c;并且通过Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题。 试想一下&#xff0c;假如商品微服务被调用较多&#xff0c;为了应对更高的并发…

【产品应用】一体化步进伺服电机在板材封边机中的应用

随着现代工业技术的快速发展&#xff0c;封边机作为木材加工行业的重要设备&#xff0c;其精度、效率和稳定性已成为衡量设备性能的重要指标。 近年来&#xff0c;一体化步进伺服电机因其高精度、高效率和强稳定性等特点&#xff0c;在封边机中得到了广泛应用。 本文将详细介绍…

1.5.1抽象java入门

前言&#xff1a; 1.5.0版本中&#xff0c;我们熟练使用Git三个可视化操作&#xff08;签出&#xff0c;提交&#xff0c;对比&#xff09;&#xff0c;再加上1.4.0版本的新建&#xff0c;总计使用四个Git可视化操作&#xff1b;对java编程的学习&#xff0c;总结&#xff0c;…

vue 前端项目调用后端接口记录

axios中不同的类型的请求附带数据使用的关键字 请求类型关键字示例GETparamsaxios({ method: get, url: example.com, params: { key: value } })POSTdataaxios({ method: post, url: example.com, data: { key: value } })PUTdataaxios({ method: put, url: example.com, dat…

MICCAI 2024 每日一篇论文 纯纯直读 CUTS:用于多粒度无监督医学图像分割的深度学习和拓扑框架

MICCAI 2024 CUTS: A Deep Learning and Topological Framework for Multigranular Unsupervised Medical Image Segmentation CUTS: 用于多粒度无监督医学图像分割的深度学习和拓扑框架 作者 陈璐1*、Matthew Amodio1*、梁博伦.沈2、冯高3、阿曼阿维斯塔4、Sanjay Aneja3,5…

[RuoYi-Vue] - 1. 项目搭建

文章目录 &#x1f42c;初始化后端项目拉取RuoYi-Vue代码Maven构建导入数据库ry-vue修改配置信息启动Redis启动项目 &#x1f30c;初始化前端项目拉取RuoYi-Vue3代码项目运行成功页面 &#x1f42c;初始化后端项目 拉取RuoYi-Vue代码 若依/RuoYi-Vue 代码地址 Maven构建 导入数…