代码随想录算法训练营第60期第五十三天打卡

article/2025/6/18 17:25:04

        大家好,我们今天来到了最后一章图论,其实图论比较难,涉及的算法也比较多,今天比较重要的就是深度优先搜索与广度优先搜索,后面的迪杰斯特拉算法等算法在我们求最短路都会涉及到,还有最近公共祖先,并查集等问题后面都会讲到,我们今天主要就看深搜与广搜的理论基础并且用这两种算法解决一道所有可达路径的问题。

第一部分深度优先搜索理论基础

        其实我们主要会涉及两种搜索算法,深搜(dfs)与广搜(bfs),我们首先要区分这两种算法,我们就先看看区别在哪里,首先告诉大家dfs是可一个方向去搜,不到黄河不回头,直到遇到绝境了,搜不下去了,再换方向(换方向的过程就涉及到了回溯)。其实我比较喜欢使用深搜,当然不排除有的题目只能使用广搜才可以解决,印象中我们代码随想录的后面就有一道叫做字符串接龙的问题,那道题目似乎就只能使用广度优先搜索解决,而bfs是先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍,搜索方向更像是广度,四面八方的搜索过程。

        其实在图上可以更加直观地显示两种搜索算法的区别:

       如果我们要搜索从起点到终点的所有路径,我们就先找到目的地6随后回溯更换方向,具体的路径大家可以去代码随想录网站去看,我就不再展示了,其实大家理解就是一条路走到黑,知道遇到终止条件。

       接下来我们就一起看看深搜三部曲,第一步确认递归函数,参数,通常我们递归的时候,我们递归搜索需要了解哪些参数,其实也可以在写递归函数的时候,发现需要什么参数,再去补充就可以。一般情况,深搜需要 二维数组数组结构保存所有路径,需要一维数组保存单一路径,这种保存结果的数组,我们可以定义一个全局变量,避免让我们的函数参数过多。其实我们今天的题目就有这种使用二维数组来保存路径的题目,第二步确认终止条件,终止条件很重要,很多同学写dfs的时候,之所以容易死循环,栈溢出等等这些问题,都是因为终止条件没有想清楚。我们一般写深搜的时候我们一般会先写终止条件,终止添加不仅是结束本层递归,同时也是我们收获结果的时候。我们这时候其实就是收获路径的时候,如果说我们使用二维数组来存储路径那么这个时候我们就可以找到一条完整的路径,我们就把这一条路径添加到二维数组里,第三步处理目前搜索节点出发的路径,这个或许就是深搜地精华,每一道题目都有特色,后面我们会讲解很多岛屿类的问题我们就会发现每一个题目都会有特色。

       关于深搜理论基础我大概就讲解这么多,剩下的我们就借助题目来理解。接下来我们看广搜的理论基础。

第二部分广度优先搜索理论基础

        广搜(bfs)是一圈一圈的搜索过程,和深搜(dfs)是一条路跑到黑然后再回溯。其实在树里面,我们对二叉树比较了解,其实广搜大家可以理解为层序遍历,因为广搜是从起点出发,以起始点为中心一圈一圈进行搜索,一旦遇到终点,记录之前走过的节点就是一条最短路。这一点很重要,只要我们使用广搜搜索到的路径就一定会是最短路径,然,也有一些问题是广搜 和 深搜都可以解决的,例如岛屿问题,这类问题的特征就是不涉及具体的遍历方式,只要能把相邻且相同属性的节点标记上就行

        其实如实告诉大家,我一般不适用广搜,我比较喜欢使用深搜,但是广搜我也必须要会,我们具体来看看广搜是如何解决具体题目的,我们就直接看看代码框架,其实我还是把示意图放到下面:

       其实大家可以很清楚看到我们是一圈一圈来搜索的,接下来我们来看代码框架,我们可以考虑使用队列,因为考虑到队列先进先出的特性我们不需要一圈逆时针一圈顺时针,如果我们使用栈的话,我们就必须一圈顺时针一圈逆时针,有时候很容易出错,

int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {queue<pair<int, int>> que; // 定义队列que.push({x, y}); // 起始节点加入队列visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点while(!que.empty()) { // 开始遍历队列里的元素pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素int curx = cur.first;int cury = cur.second; // 当前节点坐标for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历int nextx = curx + dir[i][0];int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 坐标越界了,直接跳过if (!visited[nextx][nexty]) { // 如果节点没被访问过que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问}}}}

       上面是一个代码模板,我后面会给大家重点讲解,我们是使用队列来实现。大家先做了解,我们接下来就看一道具体的题目,我先使用深搜给大家解释一遍后面的题目有的我会使用广搜。

第三部分例题对应卡码网编号98的所有可达路径

        废话不多说,我们就直接看看题目要求:

       我们就是搜索可达路径,其实我们也很清楚一点这道题目我们是规定好了边,其实我们就需要存图,这里我先告诉大家存图的话主要有两种方式,一种是邻接表一种是邻接矩阵,我们这里就使用邻接矩阵来存储,我们就先使用深搜来解决,我们是从节点1出发最后到节点n,我们要找到所有的可达路径,我们就需要一个一维数组来存储我们当前搜索到的路径,我们使用一个二维数组来存储我们搜索好的路径,我们到达终止条件的话就到了我们收获路径的时候,我就直接先给出大家深搜的解题代码,对了大家注意我们以后图论的题目大多使用ACM模式我们要注意输出格式,这里与力扣不一样了,力扣只需要我们写出核心代码,而我们在这里要写出完整的代码:

#include <iostream>
#include <vector>using namespace std;vector<vector<int>> result;
vector<int> path;void dfs(const vector<vector<int>> &grid, int x, int n)
{if (x == n){result.push_back(path);return;}for (int i = 1; i <= n; ++i){if (grid[x][i] == 1){path.push_back(i);dfs(grid, i, n);path.pop_back();}}
}int main()
{int n,m; cin >> n >> m;vector<vector<int>> grid(n + 1, vector<int>(n + 1, 0));while(m--){int s, t; cin >> s >> t;grid[s][t] = 1;}path.push_back(1);dfs(grid, 1, n);if (result.size() == 0) cout << -1 << '\n';for (const vector<int> &pa: result){for (int i = 0; i < pa.size(); ++i){if (i > 0) cout << " ";cout << pa[i];}cout << '\n';}return 0;
}

        接下来我们使用邻接表来解决这道题目:

#include <iostream>
#include <vector>
#include <list>
using namespace std;vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 1节点到终点的路径void dfs (const vector<list<int>>& graph, int x, int n) {if (x == n) { // 找到符合条件的一条路径result.push_back(path);return;}for (int i : graph[x]) { // 找到 x指向的节点path.push_back(i); // 遍历到的节点加入到路径中来dfs(graph, i, n); // 进入下一层递归path.pop_back(); // 回溯,撤销本节点}
}int main() {int n, m, s, t;cin >> n >> m;// 节点编号从1到n,所以申请 n+1 这么大的数组vector<list<int>> graph(n + 1); // 邻接表while (m--) {cin >> s >> t;// 使用邻接表 ,表示 s -> t 是相连的graph[s].push_back(t);}path.push_back(1); // 无论什么路径已经是从0节点出发dfs(graph, 1, n); // 开始遍历// 输出结果if (result.size() == 0) cout << -1 << endl;for (const vector<int> &pa : result) {for (int i = 0; i < pa.size() - 1; i++) {cout << pa[i] << " ";}cout << pa[pa.size() - 1]  << endl;}
}

       其实这道题目不适合广搜来实现,后面的题目我会考虑使用广搜实现,广搜主要是解决最短路径的问题,这道题目我就分享到这里。

今日总结

      今天我们初次接触了深搜与广搜,我们大致了解了这两宗搜索算法的不同点,我们尝试使用深搜来解决所有可达路径的问题,大家多看看就可以,这个并不难理解,我们下次再见!最后祝大家端午节快乐!


http://www.hkcw.cn/article/cYvvVBTJwl.shtml

相关文章

【Bluedriod】蓝牙协议栈GD模块(GD_SHIM_MODULE)启动机制及源码解析

本文深入剖析Android蓝牙协议栈中GD模块的启动机制&#xff0c;从模块生命周期管理、状态转换、异步初始化&#xff0c;到核心组件&#xff08;HCI层、协议栈管理器、广播/扫描/测距模块&#xff09;的协同运作。通过源码分析揭示蓝牙协议栈如何通过分层设计实现硬件抽象化、事…

threejsPBR材质与纹理贴图

1. PBR材质简介 本节课没有具体的代码&#xff0c;就是给大家科普一下PBR材质&#xff0c;所谓PBR就是&#xff0c;基于物理的渲染(physically-based rendering)。 Three.js提供了两个PBR材质相关的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial是Mes…

Leetcode 3231. 要删除的递增子序列的最小数量

1.题目基本信息 1.1.题目描述 给定一个整数数组 nums&#xff0c;你可以执行任意次下面的操作&#xff1a; 从数组删除一个 严格递增 的 子序列。 您的任务是找到使数组为 空 所需的 最小 操作数。 1.2.题目地址 https://leetcode.cn/problems/minimum-number-of-increas…

【SpringBoot实战】优雅关闭服务

文章目录 一、什么是优雅关闭&#xff1f;二、优雅关闭的核心步骤三、SpringBoot优雅关闭实现四、关键注意事项1. 超时时间必须配置2. 信号支持局限性3. 特殊请求处理 五、底层实现原理六、总结 一、什么是优雅关闭&#xff1f; 优雅关闭&#xff08;Graceful Shutdown&#x…

Redis:功能特性和应用场景

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Redis 本篇开始对于 Redis 进行正式介绍和学习 &#x1f525; 认识 Redis 在开始 Redis 学习前&#xff0c;要先认识一下 Redis Redis 的设计&#xff0c;是想要把它当做是一个数据库&#xff…

etcd详解

一、核心特性二、架构原理三、应用场景四、运维实践五、常见问题与解决方案六、与 ZooKeeper 和 Consul 的对比总结 etcd 是一个高可用的分布式键值存储系统&#xff0c;广泛应用于云原生领域&#xff0c;尤其作为 Kubernetes 的核心组件&#xff0c;用于存储集群的配置、状态和…

CTFHub-RCE 命令注入-综合练习

观察源代码 代码里面可以发现过滤了运算符、目录分隔符、分号、空格还有一些关键字也被过滤了 判断是Windows还是Linux 源代码中有 ping -c 4 说明是Linux 查看有哪些文件 用换行符的url值&#xff08;%0a&#xff09;代替分号注意&#xff1a;在url中输入 ?ip127.0.0.1%0a#…

网络编程1_网络编程引入

为什么需要网络编程&#xff1f; 用户再在浏览器中&#xff0c;打开在线视频资源等等&#xff0c;实质上说通过网络&#xff0c;获取到从网络上传输过来的一个资源。 与打开本地的文件类似&#xff0c;只是这个文件的来源是网络。相比本地资源来说&#xff0c;网络提供了更为…

性能优化 - 理论篇:性能优化的七类技术手段

文章目录 Pre引言性能优化的七类技术手段性能优化策略一览表1. 复用优化2. 计算优化2.1 并行执行2.2 变同步为异步2.3 惰性加载 3. 结果集优化3.1 数据格式与协议选择3.2 字段精简与按需返回3.3 批量处理与分页3.4 索引与位图加速 4. 资源冲突优化4.1 锁的分类与特点4.2 无锁与…

Android之ListView

1&#xff1a;简单列表(ArrayAdapter) 1&#xff1a;运行的结果&#xff1a; 2&#xff1a;首先在MyListView里面创建一个按钮&#xff0c;点击的时候进行跳转。 这里让我吃惊的是&#xff0c;Button里面可以直接设置onClick .java里面的方法。 也即是点击这个按钮之后就会去…

Unity程序集

对于Unity的程序集&#xff0c;具体内容可以参考Unity官方文档&#xff0c;程序集定义 - 预定义程序集 比如Unity的默认程序集&#xff0c;Assembly-CSharp.dll&#xff0c;还有其他的比如 Assembly-CSharp-Editor.dll&#xff0c;Assembly-CSharp-firstpass.dll 没有指定或…

【算法】递归与分治策略

一、算法整体思想 一般情况下&#xff0c;问题的规模越大&#xff0c;解题所需的计算时间越长&#xff0c;并且解题的难度可能会变得很大。 问题的规模越小&#xff0c;解题所需的计算时间往往越短&#xff0c;也比较容易处理。 当直接解决一个较大的问题时&#xff0c;有时是…

NVIDIA Mellanox BlueField-2 DPU(Data Processing Unit)智能网卡的调试和使用

专有名词 OOB&#xff1a; BMC&#xff1a; BFB&#xff1a; EMMC&#xff1a; 关键词解释eMMCEmbedded Multi-Media Card——把 NAND 闪存颗粒与控制器封装在一起的板载存储件&#xff0c;类似手机里的“内置储存” .deb&#xff1a;文件是​​Debian软件包格式​​的专…

(LeetCode 每日一题) 909. 蛇梯棋 (广度优先搜索bfs)

题目&#xff1a;909. 蛇梯棋 思路&#xff1a;广度优先搜索bfs队列&#xff0c;时间复杂度0(6*n^2)。 细节看注释 C版本&#xff1a; class Solution { public:int snakesAndLadders(vector<vector<int>>& board) {int nboard.size();// vis[i]&#xff1a;…

医疗多模态共情推理与学习一体化网络构成初探

1 引言:多模态共情推理的概念内涵与技术背景 在当今医疗人工智能领域,多模态共情推理正逐步成为突破临床决策支持系统瓶颈的关键范式。这一技术通过融合认知共情与情感共情的双重机制,模拟人类医生的综合诊断思维过程,实现对患者全方位健康状态的深度理解。医疗环境中的共…

RFID技术深度剖析:从原理、协议到S50卡与FM17550读写

知识点1【RFID的概述】 学习目标是学习对这个卡片的读写 用已有的手册实现对卡片内数据的读写操作 RFID&#xff1a;&#xff08;Radio Frequency Identification&#xff09;无线射频识别 通过无线识别目标&#xff0c;并读写相关数据&#xff0c;而无需接触 位于感知层&…

4-香豆酸:CoA连接酶晶体-文献精读138

Crystal structures of a Populus tomentosa 4-coumarate:CoA ligase shed light on its enzymatic mechanisms 杨树&#xff08;Populus tomentosa&#xff09;4-香豆酸&#xff1a;CoA连接酶的晶体结构揭示了其酶促机制 摘要 4-香豆酸&#xff1a;CoA连接酶&#xff08;4CL…

VTK|实现类似CloundCompare的测量功能

文章目录 CloundCompare在点、线、面三种模式下的显示内容✅ 图1&#xff1a;点模式✅ 图2&#xff1a;线模式✅ 图3&#xff1a;面模式 增加控制菜单栏实现测量功能类如何调用项目git链接 CloundCompare在点、线、面三种模式下的显示内容 点 线 面 三张图展示了 CloudComp…

Android15 userdebug版本不能remount

背景描述&#xff1a; 最近调试Android Vendor Hal的时候发现一个奇怪的现象: android userdebug版本刷到设备中&#xff0c;执行adb root没提示错误&#xff0c;但是没有获取到root权限。 Android设备运行的系统版本有三种情况&#xff1a;user版本、userdebug版本和eng版本…

伊朗外长:将适当回应美方核谈判提案

△伊朗外交部长阿拉格齐(资料图)当地时间5月31日,伊朗外交部长阿拉格齐在社交平台表示,当天阿曼外交大臣巴德尔访问伊朗并向其介绍了美方有关核谈判的提案。阿拉格齐表示,伊朗将根据原则、国家利益和伊朗人民的权利对此作出适当的回应。白宫新闻秘书莱维特当地时间31日表示…