图论:DFS与BFS

目录

1.DFS(图论)

1.1.DFS过程

1.2.应用

2.BFS(图论)

2.1.BFS过程

2.2.应用

2.3.双端队列BFS

实现

2.4.优先队列BFS(堆优化 Dijkstra算法)


1.DFS(图论)

DFS全称是,中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。广义上的DFS:DFS最显著的特征在于其递归调用自身。DFS 会对其访问过的点打上访问标记,在遍历图时跳过已打过标记的点,以确保每个点仅访问一次。

1.1.DFS过程

具体地说,DFS 大致结构如下:

dfs(v) // v 可以是图中的一个顶点。判断 v 是否被访问,若被访问就return在 v 上打访问标记for u in v //访问 v 的相邻节点DFS(u)//递归下一个节点,若被访问在此次递归退出

 递归实现,以邻接表为例:

vector<vector<int> > adj;//存终边为 
vector<bool> vis;//做标记
void dfs(int u) {//v为图中的一个顶点if (vis[u]) return;//表示此节点已经访问过了vis[u] = true;//标记已访问过for (int i = 0; i < adj[u].size(); i++) dfs(adj[u][i]);//遍历下一个节点
}

此递归可以用栈为遍历中节点的暂存容器来实现,以邻接表为例:

vector<vector<int> > adj;//存终边为 
vector<bool> vis;//做标记
void dfs(int u) {stack<int> st;st.push(u);vis[u] = true;//标记u被访问while (!st.empty()) {int s = st.top();//将栈顶弹出方便遍历下一节点st.pop();cout << s << ' ';//假设要遍历这个图,输出for (int v : adj[s]) {if (!vis[v]) {//表示v未被访问vis[v] = true;  // 标记v被访问过st.push(v);//将此入栈}}}
}

1.2.应用

DFS序列:DFS 序列是指 DFS 调用过程中访问的节点编号的序列。每个子树都对应 DFS 序列中的连续一段(一段区间)。

括号序列:DFS 进入某个节点的时候记录一个左括号 (,退出某个节点的时候记录一个右括号 )。每个节点会出现两次。相邻两个节点的深度相差 1。

访问图:对于非连通图,只能访问到起点所在的连通分量。对于连通图,DFS 序列通常不唯一。在 DFS 过程中,通过记录每个节点从哪个点访问而来,可以建立一个树结构,称为 DFS 树。DFS 树是原图的一个生成树。注:树的 DFS 序列也是不唯一的。

2.BFS(图论)

BFS 全称是,中文名是宽度优先搜索,也叫广度优先搜索。是图上最基础、最重要的搜索算法之一。所谓宽度优先。就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。这样做的结果是,BFS 算法找到的路径是从起点开始的最短合法路径。这条路径所包含的边数最小。在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。

2.1.BFS过程

具体地说,BFS 大致结构如下:

bfs(u) {初始化队列q将u入队标志已访问visited[u] = truewhile (队列q不为空) {出队遍历顶点的相邻顶点 {if (!visited[相邻顶点]) {//如果没有被访问过入队标志已访问visited[v] = true}}}
}

以邻接表为例:

vector<vector<int> > adj;//存终边为 
vector<bool> vis;
void bfs(int u) {queue<int> q;//建队列q.push(u);//入队vis[u] = true;//标志已经访问while (!q.empty()) {//循环每一个点的相邻顶点int s = q.front();//出队q.pop();for (int i = 0; i < adj[s].size(); i++) {//遍历相邻顶点if (!vis[adj[s][i]]) {//如果没被访问q.push(adj[s][i]);//入队vis[adj[s][i]] = true;//标志以访问}}}
}

具体来说,我们用一个队列 q 来记录要处理的节点,然后开一个布尔数组 vis[] 来标记是否已经访问过某个节点。开始的时候,我们将所有节点的 vis 值设为 false,表示没有访问过;然后把起点 u 放入队列 Q 中并将 vis[s] 设为 true。之后,我们每次从队列 q 中取出队首的节点 s,然后把与 s 相邻的所有节点 v 标记为已访问过并放入队列 q。循环直至当队列 q 为空,表示 BFS 结束。

在 BFS 的过程中,也可以记录一些额外的信息。

2.2.应用

1.open-closed表:在实现 BFS 的时候,本质上我们把未被访问过的节点放在一个称为 open 的容器中,而把已经访问过了的节点放在一个称为 closed 容器中。

2.访问图:对于非连通图,只能访问到起点所在的连通分量。对于连通图,BFS 序列通常不唯一。可以定义 BFS 树:在 BFS 过程中,通过记录每个节点从哪个点访问而来,可以建立一个树结构,即为 BFS 树。如果原图不连通,只能访问到从起点出发能够到达的点。

3.在一个无权图上求从起点到其他所有点的最短路径。

4.在O(n+m)时间内求出所有连通块。

5.如果把一个游戏的动作看做是状态图上的一条边,那么 BFS 可以用来找到在游戏中从一个状态到达另一个状态所需要的最小步骤。

6.在一个有向无权图中找最小环。 (从每个点开始 BFS,在我们即将抵达一个之前访问过的点开
始的时候,就知道遇到了一个环。图的最小环是每次 BFS 得到的最小环的平均值。)。

7.找到一定在(a,b)最短路上的边。 (分别从a和 b 进行 BFS,得到两个d数组之后对每一条
边(u,v),如果 d_{a}[u]+1+d_{b}[v]=d_{a}[b],则说明该边在最短路上)。

8.找到一定在(a,b)最短路上的点。 (分别从a和b 进行 BFS,得到两个 d 数组。之后对每一个
点v,如果 d_{a}[v]+d_{b}[v]=d_{a}[b],则说明该点在某条最短路上)。

9.找到一条长度为偶数的最短路。(我们需要一个构造一个新图,把每个点拆成两个新点,原图
的边(u,u)变成((u,0),(,1))和((u,1),(u,0))。对新图做 BFS,(s,0)和(t,0) 之间的最短路即为所求)。

10.在一个边权为0/1的图上求最短路,见下方双端队列BFS。

2.3.双端队列BFS

边权值为可能有,也可能没有(由于 BFS 适用于权值为 1 的图,所以一般权值是 0 或 1),或者能够转化为这种边权值的最短路问题。例如在走迷宫问题中,你可以花 1 个金币走 5 步,也可以不花金币走 1 步,这就可以用 0-1 BFS 解决。

实现

一般情况下,我们把没有权值的边扩展到的点放到队首,有权值的边扩展到的点放到队尾。这样即可保证像普通 BFS 一样整个队列队首到队尾权值单调不下降。

while (队列不为空) {int u = 队首;出队遍历顶点的相邻顶点 {更新数据if (没权值)添加到队首;else添加到队尾;}
}

2.4.优先队列BFS(堆优化 Dijkstra算法)

优先队列,相当于一个二叉堆,STL 中提供了std::priority_queue,可以方便我们使用优先队列。

在基于优先队列的 BFS 中,我们每次从队首取出代价最小的结点进行进一步搜索。容易证明这个贪心思想是正确的,因为从这个结点开始扩展的搜索,一定不会更新原来那些代价更高的结点。换句话说,其余那些代价更高的结点,我们不回去考虑更新它。

当然,每个结点可能会被入队多次,只是每次入队的代价不同。当该结点第一次从优先队列中取出,以后便无需再在该结点进行搜索,直接忽略即可。所以,优先队列的 BFS 当中,每个结点只会被处理一次。

相对于普通队列的 BFS,为了维护这个优先队列,时间复杂度多了一个logn。不过普通 BFS 有可能每个结点入队、出队多次,时间复杂度会达到 O(n^{2}),不是 O(n)。所以优先队列 BFS 通常还是快的。

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

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

相关文章

0基础安装Burpsuit专业版

首先先安装java环境,安装jdk 11的版本 文件中2023版的可以直接点开使用不需要复杂的操作的步骤 资源获取链接&#xff1a; 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;k2qq 其中&#xff1a;1号文件是bp的英文版激活包&#xff0c;-2号是中文版汉化版的激活包…

鸿蒙开发实战:【音频组件】

简介 音频组件用于实现音频相关的功能&#xff0c;包括音频播放&#xff0c;录制&#xff0c;音量管理和设备管理。 图 1 音频组件架构图 基本概念 采样 采样是指将连续时域上的模拟信号按照一定的时间间隔采样&#xff0c;获取到离散时域上离散信号的过程。 采样率 采样…

FreeRTOS学习第10篇--队列使用示例

FreeRTOS学习第10篇–队列使用示例 本文目标&#xff1a;FreeRTOS学习第10篇–队列使用示例 按照本文的描述&#xff0c;可以进行简单的使用队列。 本文实验条件&#xff1a;拥有C语言基础&#xff0c;装有编译和集成的开发环境&#xff0c;比如&#xff1a;Keil uVision5 …

KKView远程控制: todesk内网穿透

Todesk内网穿透&#xff1a;实现远程访问的新途径 在数字化时代&#xff0c;远程访问已成为许多企业和个人的基本需求。Todesk作为一款远程桌面控制软件&#xff0c;其内网穿透功能为用户提供了便捷、安全的远程访问体验。本文将介绍Todesk内网穿透的原理、应用场景及其优势&a…

【机器学习】走进监督学习:构建智能预测模型的第一步

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

2024年【天津市安全员C证】考试内容及天津市安全员C证考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 天津市安全员C证考试内容是安全生产模拟考试一点通生成的&#xff0c;天津市安全员C证证模拟考试题库是根据天津市安全员C证最新版教材汇编出天津市安全员C证仿真模拟考试。2024年【天津市安全员C证】考试内容及天津市…

开发反应式API

开发反应式API 开发反应式API1 使用SpringWebFlux1.1 Spring WebFlux 简介1.2 编写反应式控制器 2 定义函数式请求处理器3 测试反应式控制器3.1 测试 GET 请求3.2 测试 POST 请求3.3 使用实时服务器进行测试 4 反应式消费RESTAPI4.1 获取资源4.2 发送资源4.3 删除资源4.4 处理错…

APP在应用商店该如何做好节日营销

38妇女节刚刚过去&#xff0c;不少商家吃上了一波节日红利。 你有没有注意到很多App在应用商店里改头换面&#xff0c;开展了很多以“三八节”为主题的营销活动&#xff0c;并且取得了不错的成绩。 可见季节性营销策划对产品的下载量和用户留存率还是很重要的。 那么我们如何…

数据结构知识点汇总(持续更新版)

数据结构 一、绪论 检测知识&#xff1a; 1.1基本概念 以前的计算机 弹道计算机 现如今 主要运用于非数值的计算 基本概念和术语 数据&#xff1a;是信息的载体&#xff0c;描述客观事物属性的值&#xff0c;字符以及所有能输入到计算机中并被计算机程序识别和处理的符号的…

CorelDRAW2024中文版全新来袭,让你的设计创意无限!

一、CorelDRAW软件的概述 CorelDRAW是一款由加拿大Corel公司开发的矢量图形编辑软件&#xff0c;自1989年以来&#xff0c;一直以其强大的功能和易用性深受广大设计师的喜爱。CorelDRAW软件不仅适用于个人创作者&#xff0c;还广泛应用于各类企业、出版社、教育机构等领域&…

考研C语言复习进阶(5)

目录 1. 为什么使用文件 2. 什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3. 文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 4. 文件的顺序读写 ​编辑 ​编辑 4.1 对比一组函数&#xff1a; ​编辑 5. 文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind…

【MySQL基础】MySQL基础操作二

文章目录 &#x1f34e;1.数据库约束&#x1f350;约束类型&#x1f346;1.1NOT NULL&#x1f346;1.2UNIQUE&#x1f346;1.3DEFAULT&#x1f346;1.4PRIMARY KEY&#x1f346;1.5FOREIGN KEY &#x1f34f;2.查询操作&#x1f35f;2.1聚合查询&#x1f354;2.1.1聚合函数&…