图论:DFS与BFS

news2025/1/16 3:43:43

目录

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] = true
  while (队列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.coloradmin.cn/o/1519283.html

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

相关文章

【每日力扣】235. 二叉搜索树的最近公共祖先与39. 组合总和问题描述

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义…

如何在Linux上使用git远程上传至gitee托管(add-commit-push指令详解)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

自动驾驶泊车(APA_HAVP)算法学习整理

自动驾驶泊车(APA/HAVP)算法学习整理 附赠宝贵的全套自动驾驶学习资料&#xff1a;链接

「SpringBrick快速入门指南」:☀️ 后端领域新兴技术璀璨之星☀️ 基于Spring Boot的高级插件化开发框架

文章目录 关于 | About技术文档 | Document开源项目 | Project 案例 | Demo项目结构 | Structure主程序配置集成 | Settings引入框架依赖 | Framework在配置文件加入配置 | YamlSpringBoot启动类改引导类 | Change 插件配置集成 | Settings引入依赖 | XML定义插件引导类 | Clas…

从混沌到秩序——90年代中国数据库的激烈角逐

引言 在数字化浪潮的推动下&#xff0c;数据库技术已成为支撑数字经济的坚实基石。腾讯云TVP《技术指针》联合《明说三人行》特别策划的直播系列——【中国数据库前世今生】&#xff0c;我们将通过五期直播&#xff0c;带您穿越五个十年&#xff0c;深入探讨每个时代的数据库演…

Android NDK入门:在应用中加入C和C++的力量

目录 ​编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师&#xff0c;却从来没有真正仔细了解过NDK&#…

【LeetCode热题100】24. 两两交换链表中的节点(链表)

一.题目要求 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 二.题目难度 中等 三.输入样例 示例 1&#xff1a; 输入&am…

AI视频批量混剪系统|罐头鱼AI视频矩阵获客

AI视频批量混剪系统助您轻松管理和编辑视频素材 如今&#xff0c;视频营销已成为企业推广的重要方式。为了满足用户对视频管理、发布和编辑的需求&#xff0c;《罐头鱼AI视频批量混剪系统》应运而生。这款智能化系统集成了多种功能&#xff0c;助您轻松管理和发布精彩视频内容…

JavaScript 之 获取当前日期的周日期范围、月日期范围

前言 实际开发中&#xff0c;有的时候产品要求需要用到日期筛选&#xff0c;日期筛选又需要用的当前日期的周日期范围&#xff0c;也有可能上一周&#xff0c;下一周这样的&#xff0c;相对应的也就又可能是当前日期的月日期范围&#xff0c;上一个月、下一个月的这样的&#x…

[NSSRound#18 Basic]web解析

文章目录 门酱想玩什么呢&#xff1f;Becomeroot 门酱想玩什么呢&#xff1f; 打开题目&#xff0c;加载完视频后要求我们给个游戏链接 点开评论区不难发现应该是想玩元梦之星&#xff0c;这里有个评论功能可以上传图片 我们随便输入点东西发现是插入并赋值到content元素里面 …

识别恶意IP地址的有效方法

在互联网的环境中&#xff0c;恶意IP地址可能会对网络安全造成严重威胁&#xff0c;例如发起网络攻击、传播恶意软件等。因此&#xff0c;识别恶意IP地址是保护网络安全的重要一环。IP数据云将探讨一些有效的方法来识别恶意IP地址。 IP地址查询&#xff1a;https://www.ipdata…

计算机毕业设计项目基于大数据和ALS算法实现的房源智能推荐系统

概要 目前&#xff0c;现有的房源信息不够透明化大多中介混淆市场&#xff0c;内含不为人知的商业链。有经验的租客们会通过周边房价走势和走访周边房源对比调研、筛选适合自己的房源。 同时&#xff0c;对于用户工作地点需求和各种人群类型如大学生群体&#xff0c;年轻小资&a…

单⽬相机成像过程_看这一篇就够了

单⽬相机成像过程:看这一篇就够了 附赠宝贵的全套自动驾驶学习资料&#xff1a; 资料链接 附赠宝贵的全套自动驾驶学习资料&#xff1a; 资料链接

【LeetCode热题100】54. 螺旋矩阵

一.题目要求 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 二.题目难度 中等 三.输入样例 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 示例…

04-java基础--流程控制语句

一、switch语句 二、循环的三种结构 流程控制语句分为三种结构&#xff1a; 顺序结构&#xff08;按代码的书写顺序执行&#xff0c;从上到下依次执行&#xff09;分支结构&#xff08;if语句、if–else语句、switch语句&#xff09;循环结构&#xff08;while、for循环、do–…

Java 设计模式系列:行为型-观察者模式

简介 观察者模式是一种行为型设计模式&#xff0c;又被称为发布-订阅&#xff08;Publish/Subscribe&#xff09;模式&#xff0c;它定义了对象之间的一对多依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都会得到通知并被自动更新。 观察者…

七月论文审稿GPT第3.2版和第3.5版:通过paper-review数据集分别微调Mistral、gemma

前言 我司第二项目组一直在迭代论文审稿GPT(对应的第二项目组成员除我之外&#xff0c;包括&#xff1a;阿荀、阿李、鸿飞、文弱等人)&#xff0c;比如 七月论文审稿GPT第1版&#xff1a;通过3万多篇paper和10多万的review数据微调RWKV七月论文审稿GPT第2版&#xff1a;用一万…

wps珠海市政府版本

功能 无广告&#xff0c;安装直接使用&#xff0c;word,excel,ppt功能齐全 步骤 双击exe文件&#xff0c;更改安装步骤即可&#xff0c;任意选择一个部门就可以了 获取资源 链接&#xff1a;https://pan.baidu.com/s/1IVfNVgLwsp5QBT2uX-yROQ?pwdme6f 提取码&#xff1a;me…

上传图片时系统提示尺寸太大无法上传?证件照怎么改kb大小?

大家好&#xff0c;相信大家网上因为各式各样的原因上传证件照的时候&#xff0c;总是会提示尺寸过大或尺寸不符合等情况&#xff0c;例如教师资格证的照片要求是&#xff1b;格式为jpg/jpeg&#xff0c;不大于200K&#xff0c;宽高为150*200像素&#xff0c;比例约为3:4。怎么…

Android 监听卫星导航系统状态及卫星测量数据变化

源码 package com.android.circlescalebar;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.conte…