【数据结构与算法】BFS 和 DFS

news2025/2/27 23:14:42

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:数据结构与算法
🌠 首发时间:2022年11月16日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🌟 一以贯之的努力 不得懈怠的人生

阅读指南

  • 基本操作
  • 广度优先遍历(BFS)
    • 与树的广度优先遍历之间的联系
    • 算法实现
    • 复杂度分析
    • 广度优先生成树
  • 深度优先遍历(DFS)
    • 与树的深度优先遍历之间的联系
    • 算法实现
    • 复杂度分析
    • 深度优先遍历序列
    • 深度优先生成树
    • 图的遍历和图的连通性

基本操作

  • A d j a c e n t ( G , x , y ) Adjacent(G, x, y) Adjacent(G,x,y):判断图 G G G 是否存在边 < x , y > <x, y> <x,y> ( x , y ) (x, y) (x,y)
  • N e i g h b o r s ( G , x ) Neighbors(G, x) Neighbors(G,x):列出图 G G G 中与结点 x x x 邻接的边
  • I n s e r t V e r t e x ( G , x ) InsertVertex(G, x) InsertVertex(G,x):在图 G G G 中插入顶点 x x x
  • D e l e t e V e r t e x ( G , x ) DeleteVertex(G, x) DeleteVertex(G,x):在图 G G G 中删除顶点 x x x
  • A d d E d g e ( G , x , y ) AddEdge(G, x, y) AddEdge(G,x,y):若无向边 ( x , y ) (x, y) (x,y) 或有向边 < x , y > <x, y> <x,y> 不存在,则向图 G G G 中添加该边
  • R e m o v e E d g e ( G , x , y ) RemoveEdge(G, x, y) RemoveEdge(G,x,y):若无向边 ( x , y ) (x, y) (x,y) 或有向边 < x , y > <x, y> <x,y> 存在,则从图 G G G 中删除该边
  • F i r s t N e i g h b o r ( G , x ) FirstNeighbor(G, x) FirstNeighbor(G,x):求图 G G G 中顶点 x x x 的第一个邻接点,若有则返回顶点号;若 x x x 没有邻接点或图中不存在 x x x,则返回 − 1 -1 1
  • N e x t N e i g h b o r ( G , x , y ) NextNeighbor(G, x, y) NextNeighbor(G,x,y):假设图 G G G 中顶点 y y y 是顶点 x x x 的一个邻接点,返回除了 y y y 之外顶点 x x x 的下一个邻接点的顶点号,若 y y y x x x 的最后一个邻接点,则返回 − 1 -1 1
  • G e t e d g e v a l u e ( G , x , y ) Get_edge_value(G, x, y) Getedgevalue(G,x,y):获取图 G G G 中边 ( x , y ) (x, y) (x,y) < x , y > <x, y> <x,y> 对应的权值
  • S e t e d g e v a l u e ( G , x , y , v ) Set_edge_value(G, x, y, v) Setedgevalue(G,x,y,v):设置图 G G G 中边 ( x , y ) (x, y) (x,y) < x , y > <x, y> <x,y> 对应的权值为 v v v

广度优先遍历(BFS)

与树的广度优先遍历之间的联系

树的广度优先遍历,也就是树的层次遍历,有以下步骤:

  1. 若树非空,则根节点入队
  2. 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队
  3. 重复第 2 2 2 步指导队列为空

对于树,由于树不存在 “回路”,因此在搜索相邻的结点时,不可能搜索到已经访问过的结点;而图则反之,所以我们需要对图中的结点进行标记,以此来识别这个结点是否访问过

广度优先遍历( B r e a d t h − F i r s t − S e a r c h , B F S Breadth-First-Search, BFS BreadthFirstSearch,BFS)要点:

  1. 找到与一个顶点相邻的所有顶点
  2. 标记哪些顶点被访问过
  3. 需要一个辅助队列

在广度优先遍历中,我们需要用到两个基本操作:

  • F i r s t N e i g h b o r ( G , x ) FirstNeighbor(G, x) FirstNeighbor(G,x):求图 G G G 中顶点 x x x 的第一个邻接点,若有则返回顶点号;若 x x x 没有邻接点或图中不存在 x x x,则返回 − 1 -1 1
  • N e x t N e i g h b o r ( G , x , y ) NextNeighbor(G, x, y) NextNeighbor(G,x,y):假设图 G G G 中顶点 y y y 是顶点 x x x 的一个邻接点,返回除了 y y y 之外顶点 x x x 的下一个邻接点的顶点号,若 y y y x x x 的最后一个邻接点,则返回 − 1 -1 1

算法实现

bool visited[MAX_VERTEX_NUM];	//访问标记数组,初始值都为false

void BFSTraverse(Graph G) {		//对图G进行广度优先遍历
	for (i = 0; i < G.vexnum; ++i) {
		visited[i] = false;		//访问标记数组初始化
	}
	InitQueue(Q);				//初始化辅助队列Q
	for (i = 0; i < G.vexnum; ++i) {	//从0号顶点开始遍历
		if (!visited[i]) 				//对每个连通分量调用一次BFS
			BFS(G, i);					//vi未访问过,从vi开始BFS
	}
}
		
//广度优先遍历
void BFS(Graph G, int v) {		//从顶点v出发,广度优先遍历图
	visit(v);					//访问初始顶点v
	visited[v] = true;			//对v做已访问标记
	Enqueue(Q, v);				//顶点v入队列Q
	while (!isEmpty(Q)) {
		DeQueue(Q, v);			//顶点v出队
		for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) {
			//检测v所有邻接点
			if (!visited[w]) {				//w为v的未访问过的邻接顶点
				visit(w);					//访问顶点w
				visited[w] = true;			//对w做已访问标记
				Enqueue(Q, w);				//顶点w入队列Q
			}//if
		}//for
	}//while
}	

结论:对于无向图,调用 B F S BFS BFS 函数的次数 = = = 连通分量数(连通分量:一个极大连通子图为一个连通分量)

复杂度分析

空间复杂度:

  • 最坏情况,我们访问第一个顶点时,所有顶点都和它连通,此时辅助队列大小为 O ( ∣ V ∣ ) O(|V|) O(V)

如果我们是用邻接矩阵存储的图:

在这里插入图片描述

  • 访问 ∣ V ∣ |V| V 个顶点需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间
  • 查找每个顶点的邻接点都需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间,而总共有 ∣ V ∣ |V| V 个顶点
  • 时间复杂度 = O ( ∣ V ∣ 2 ) = O(|V|^2) =O(V2)

如果我们是用邻接表存储的图:
在这里插入图片描述

  • 访问 ∣ V ∣ |V| V 个顶点需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间
  • 查找每个顶点的邻接点共需要 O ( ∣ E ∣ ) O(|E|) O(E) 的时间
  • 时间复杂度 = O ( ∣ V ∣ + ∣ E ∣ ) = O(|V| + |E|) =O(V+E)

广度优先生成树

在这里插入图片描述

广度优先生成树由广度优先遍历过程确定。由于邻接表的表示方式不唯一,因此基于邻接表的广度优先生成树也不唯一

对于非连通图的广度优先遍历,我们还可以得到广度优先生成森林

深度优先遍历(DFS)

与树的深度优先遍历之间的联系

树的深度优先遍历分为先根遍历和后根遍历,图的深度优先遍历和树的先根遍历比较相似

//树的先根遍历
void PreOrder(TreeNode *R) {
	if (R != NULL) {
		visit(R);		//访问根节点
		while (R还有下一个子树T)
			PreOrder(T);	//先根遍历下一棵子树
	}
}

算法实现

bool visited[MAX_VERTEX_NUM];	//访问标记数组

void DFSTraverse(Graph G) {				//对图G进行深度优先遍历
	for (v = 0; v < G.vexnum; ++v)		//初始化已访问标记数据
		visited[v] = false;
	for (v = 0; v < G.vexnuj; ++v)		//解决非连通图无法遍历完的问题
		if (!visited[v]) DFS(G, v);
}

void DFS(Graph G, int v) {		//从顶点v出发,深度优先遍历图G
	visit(v);					//访问顶点v
	visited[v] = true;			//设置已访问标记
	for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) {
		if (!visited[w]) {		//w为v的未访问的邻接顶点
			DFS(G, w);
		}
	}
}

复杂度分析

空间复杂度:来自函数调用栈,最坏情况下,递归深度为 O ( ∣ V ∣ ) O(|V|) O(V);最好情况为 O ( 1 ) O(1) O(1)

时间复杂度 = = = 访问每个节点所需时间 + + + 探索每条边所需时间

如果是用邻接矩阵存储的图:

  • 访问 ∣ V ∣ |V| V 个顶点需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间
  • 查找每个顶点的邻接点都需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间,总共有 ∣ V ∣ |V| V 个顶点
  • 总时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)

如果是用邻接表存储的图:

  • 访问 ∣ V ∣ |V| V 个顶点需要 O ( ∣ V ∣ ) O(|V|) O(V) 的时间
  • 查找每个顶点的邻接点共需要 O ( ∣ E ∣ ) O(|E|) O(E) 的时间
  • 时间复杂度 = O ( ∣ V ∣ + ∣ E ∣ ) = O(|V| + |E|) =O(V+E)

你会发现,深度优先遍历和广度优先遍历的复杂度是一样的

深度优先遍历序列

在这里插入图片描述

对于上图:

  • 1 1 1 出发的深度优先遍历序列为: 1 , 2 , 6 , 3 , 4 , 7 , 8 , 5 1, 2, 6, 3, 4, 7, 8, 5 1,2,6,3,4,7,8,5
  • 2 2 2 出发的深度优先遍历序列为: 2 , 1 , 5 , 6 , 3 , 4 , 7 , 8 2, 1, 5, 6, 3, 4, 7, 8 2,1,5,6,3,4,7,8
  • 3 3 3 出发的深度优先遍历序列为: 3 , 4 , 7 , 6 , 2 , 1 , 5 , 8 3, 4, 7, 6, 2, 1, 5, 8 3,4,7,6,2,1,5,8

注意:如果邻接表不一样,深度优先遍历序列也可能不一样;同时,因为邻接矩阵表示方式唯一,所以深度优先遍历序列唯一

深度优先生成树

将深度优先遍历序列写成树的形式,即为对应的深度优先生成树

如果是非连通图,那么会有深度优先生成森林

图的遍历和图的连通性

对无向图进行 B F S / D F S BFS/DFS BFS/DFS 遍历,调用 B F S / D F S BFS/DFS BFS/DFS 函数的次数等于连通分量数;如果是连通图的话,我们只需要调用一次 B F S / D F S BFS/DFS BFS/DFS 函数

对有向图进行 B F S / D F S BFS/DFS BFS/DFS 遍历,调用 B F S / D F S BFS/DFS BFS/DFS 函数的次数要根据具体的数进行具体分析;如果起始顶点到其他顶点都有路径,那么我们只需要调用一次函数即可

对于强连通图,我们从任何一个顶点出发都只需要调用一次 B F S / D F S BFS/DFS BFS/DFS

在这里插入图片描述

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

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

相关文章

关于混合SDN网络的统一信息模型方面研究事件通知的多样性问题

关于混合SDN网络的统一信息模型方面研究事件通知的多样性问题面向混合SDN中异构设备的混合域发现协议Hybrid Domain Discovery Protocol (HDDP)HDDP的主要特点混合域发现协议(HDDP)协议介绍过程总结申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引…

阿里云研发工程师刘睿:阿里云消息生态及最佳实践

2022 年 9 月 24 日&#xff0c;阿里云用户组&#xff08;AUG&#xff09;第 12 期活动在厦门举办。活动现场&#xff0c;阿里云消息中间件研发工程师刘睿&#xff0c;向参会企业代表分享了阿里云消息生态及最佳实践。本文根据演讲内容整理而成。 众所周知&#xff0c;消息中间…

智能生活 App 垂直品类- IPC SDK 架构及快速集成配置(安卓版)

除了通用设备功能的应用开发&#xff0c;针对部分常见的全屋智能场景设备&#xff0c;智能生活 App SDK 提供了单独的垂直品类 SDK。包括智能摄像机 SDK、智能门锁 SDK、扫地机机器人 SDK、智能照明控制 SDK 等。 基于 智能生活 App SDK&#xff0c;垂直品类 SDK 面向这些特定…

适合编程初学者的开源博客系统(Flutter版)

目标 为编程初学者打造入门学习项目&#xff0c;使用各种主流编程语言来实现。让想学编程的&#xff0c;一个都不落下。 上述基本涵盖了当前编程开发所有主流语言&#xff08;我这里说的主流&#xff0c;直白点讲就是互联网公司都在用的&#xff09;。 左侧为前端版本&#x…

即将学习3D建模看过来,超高性价比电脑推荐

虽说建模还是台式最好&#xff0c;但是需要外出或者带回家工作的时候还是不方便的❌&#xff0c;咱们来看下怎么挑&#xff5e; 我们使用的建模软件为3DsMax、maya、zbrush等 建模的时候&#xff0c;对显卡和内存的要求较高&#x1f250; 显卡越好&#xff0c;模型面数多了也…

pwn调试环境搭建

应用场景 不同的pwn题有不同的环境要求&#xff0c;堆题要求更多&#xff0c;patchelf可以满足一定场景的实现&#xff0c;但终究不如在实际对应的环境中搭建。如何快速搭建一个高效节省资源的环境也是一个不小的问题。 方法 高效节省资源&#xff1a;一个主要调试环境多个运…

CE修改植物大战僵尸-天上无限掉落阳光(小宇特详解)

CE修改植物大战僵尸-天上无限掉落阳光 里是CE修改植物大战僵尸的第三个博客&#xff0c;其他的操作请看我之前的博客。 天上无限掉落阳光。 这里说明一下版本&#xff1a; CE&#xff1a;CE6.8 植物大战僵尸版本&#xff1a;植物大战僵尸95版 具体步骤 我们在刚出现阳光…

英国Essay写作降重方法有哪些?

英国留学生写Essay的时候会遇到抄袭率比较高的情况&#xff0c;当参考别人的文章的同时&#xff0c;不知不觉也就会让文章的重复率有所增加&#xff0c;有效减少重复率修改的方法你清楚吗&#xff1f;下文为大家讲解一下降低英国Essay写作降重的方法。 When British overseas s…

汇编语言实验8:BIOS/DOS功能调用与宏指令程序设计

《汇编语言程序设计》实验报告实验八 BIOS/DOS功能调用与宏指令程序设计1. 实验目的2. 实验任务3.1 BIOS/DOS及宏汇编程序题目设计3.2 编写代码3.3 代码分析3.4 运行测试4. 实验总结实验八 BIOS/DOS功能调用与宏指令程序设计 1. 实验目的 掌握汇编语言程序设计的基本方法和技…

基于工业级4G5G路由器大型设备远程无线监控方案

一、行业背景进入21世纪&#xff0c;我国经济持续高速发展&#xff0c;全球各地的企业对大型设备的需求日益旺盛&#xff0c;例如工业锅炉、数控车床、医疗器械等&#xff0c;在产品服务业全球的时候&#xff0c;产品厂家需要对所有在外的设备进行远程监控管理&#xff0c;实时…

Linux 内存管理知识总结(二)

Linux 内存管理框架 传统的多核运算是使用 SMP(Symmetric Multi-Processor )模式&#xff1a;将多个处理器与一个集中的存储器和 I/O 总线相连&#xff0c;所有处理器访问同一个物理存储器&#xff0c;因此 SMP 系统有时也被称为一致存储器访问&#xff08;UMA&#xff09;结构…

Leetcode刷题day1|数组一|704.二分查找,27.移除元素,35.搜索插入位置

文章目录一、面试中数组相关理论知识二、二分查找问题思路注意事项AC代码三、移除元素思路注意事项AC代码四、寻找插入位置思路AC代码五、总结二分法|二分查找法|二分搜索法|二分易错点相关概念代码实现一、面试中数组相关理论知识 数组是非常基础的数据结构&#xff0c;在面试…

【树状数组】前缀和问题

一、引子 给你一个数组 nums &#xff0c;请你完成两类查询。 其中一类查询要求更新数组 nums 下标对应的值另一类查询要求返回数组 nums 中索引 left 和索引 right 之间&#xff08; 包含 &#xff09;的nums元素的和 题目只是一个用来检测我们思想的东西&#xff0c;比如这…

Android BottomSheet总结

文章目录Android BottomSheet总结BottomSheetBottomSheetDialogBottomSheetDialogFragment全屏无阴影BottomSheetDialogFragment代码下载Android BottomSheet总结 BottomSheet XML布局&#xff1a; <?xml version"1.0" encoding"utf-8"?> <an…

C++ 条件变量的使用

绪论 并发编程纷繁复杂&#xff0c;其中用于线程同步的主要工具——条件变量&#xff0c;虽然精悍&#xff0c;但是要想正确灵活的运用却并不容易。 对于条件变量的理解有三个难点&#xff1a; 为什么wait函数需要将解锁和阻塞、唤醒和上锁这两对操作编程原子的&#xff1f;为…

MCMC学习笔记-马尔科夫链概述

参考文章&#xff1a;MCMC(二)马尔科夫链 - 刘建平Pinard - 博客园 写给小白看的马尔科夫链&#xff08;Markov Chain&#xff09;最佳入门教程_许进进的博客-CSDN博客_markov链 目录 1.马尔科夫链概述 1.1股票市场模型 2.马尔科夫链模型状态转移矩阵的性质 (本节重点) 2.…

小程序容器技术加持下,企业自主打造小程序生态

小程序是一种不用下载就能使用的应用&#xff0c;也是一项门槛非常高的创新&#xff0c;经过将近两年的发展&#xff0c;已经构造了新的小程序开发环境和开发者生态。 据对公开资料进行统计&#xff0c;2021年全网小程序数量已超700万&#xff0c;其中微信小程序开发者突破300…

java使用world模板动态生成PDF文件

根据项目需求&#xff0c;需要用到一个功能&#xff0c;根据页面参数需要动态的生成一个world&#xff0c;并将world生成两份PDF文件&#xff0c;一份正式文件&#xff0c;一份临时的电子文件&#xff08;带有二维码&#xff0c;扫描可以下载正式文件的电子版本&#xff09;。同…

JAVA开发(nginx)

主要描述下面4个内容&#xff1a; 1.Nginx的正向代理和反向代理 2.Nginx的动静分离 3.Nginx的负载均衡 4.Nginx的配置详解 解释&#xff1a; Nginx的正向代理&#xff1a;代理的是客户端。 Nginx的反向代理&#xff1a;代理的是服务端。在web服务中&#xff0c;一般都是…