图的深度优先遍历和广度优先遍历

article/2025/9/20 6:58:09

本文参考自《大话数据结构》

文章目录

    • 定义
    • 图的存储结构
      • 邻接矩阵
      • 邻接表
    • 图的遍历
      • 深度优先遍历
        • 邻接矩阵代码
        • 邻接表代码
      • 广度优先遍历
        • 邻接矩阵
        • 邻接表
    • 最小生成树
    • 最短路径算法

定义

图(Graph) 是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合

图按照有无方向分为无向图有向图 。无向图由顶点和边构成,有向图由顶点和弧构成。弧有弧尾和弧头之分。
图按照边或弧的多少分稀疏图稠密图 。如果任意两个顶点之间都存在边叫完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。
图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做,有向图顶点分为入度和出度。图上的边或弧上带权则称为网。
图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图 ,有向则称强连通图。图中有子图,若子图极大连通则就是连通分量,有向的则称强连通分量。
无向图中连通且n个顶点n-1条边叫生成树 。有向图中一顶点入度为 0其余顶点入度为1的叫有向树。一个有向图由若干棵有向树构成生成森林。

图的存储结构

邻接矩阵

图的邻接矩阵(Adjacency Matrix) 存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。

typedef char VertexType;//顶点类型
typedef int EdgeType;//边的权值类型
#define MAXVEX 100//最大顶点数
#define INFINITY 65535//用65535表示∞
typedef struct {VertexType vexs[MAXVEX];//顶点表EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵int numVertexes, numEdges;//图中当前的顶点数和边数
}MGraph;

邻接表

邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。

因此我们考虑另外-种存储结构方式。回忆我们在线性表时谈到,顺序存储结构就存在预先分配内存可能造成存储空间浪费的问题,于是引出了链式存储的结构。同样的,我们也可以考虑对边或弧使用链式存储的方式来避免空间浪费的问题。

再回忆我们在树中谈存储结构时,讲到了一种孩子表示法, 将结点存入数组,并对结点的孩子进行链式存储,不管有多少孩子,也不会存在空间浪费问题。这个思路同样适用于图的存储。我们把这种数组与链表相结合的存储方法称为邻接表(Adjacency List)。

邻接表的处理办法是这样的:

  1. 图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易地读取顶点信息,更加方便。另外,对于顶点数组中,每个数据元素还需要存储指向第一一个邻接点的指针,以便于查找该顶点的边信息。
  2. 图中每个顶点Vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点v1的边表,有向图则称为顶点VI作为弧尾的出边表。

图片来源:《大话数据结构》
若是有向图,邻接表结构是类似的,比如图7-4-7 中第一幅图的 邻接表就是第二幅图。但要注意的是有向图由于有方向,我们是以顶点为弧尾来存储边表的,这样很容易就可以得到每个顶点的出度。但也有时为了便于确定顶点的入度或以顶点为弧头的弧,我们可以建立一个有向图的逆邻接表,即对每个顶点Vi都建立一个链接为v为弧头的表。
图片来源:《大话数据结构》
对于带权值的网图,可以在边表结点定义中再增加一一个 weight的数据域,存储权对于带权值的网图,可以在边表结点定义中再增加一一个 weight的数据域,存储权值信息即可,如图7-4-8所示。
在这里插入图片描述

typedef struct EdgeNode {//边表节点int adjvex;//邻接点域,存储该顶点对应的下标EdgeType weight;//用于存储权值,对于非网图可以不需要struct EdgeNode* next;//指向下一个邻接点
}EdgeNode;typedef struct VertexNode {//顶点表节点VertexType data;//顶点域,存储顶点信息EdgeNode* firstedge;//边表头指针
}VertexNode, AdjList[MAXVEX];typedef struct {AdjList adjList;int numVertexes, numEdges;//图中当前的顶点数和边数
}GraphAdjList;

图的遍历

从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次, 这一过程就叫做图的遍历(Traversing Graph)。

深度优先遍历

深度优先遍历(Depth, First Search),也有称为深度优先搜索,简称为DFS。

它就是。它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。事实上,我们这里讲到的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后, 若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

邻接矩阵代码

#define TRUE 1
#define FALSE 0
#define MAXVEX 100//最大顶点数typedef int Boolean;
typedef char VertexType;//顶点类型
typedef int EdgeType;//边的权值类型typedef struct {VertexType vexs[MAXVEX];//顶点表EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵int numVertexes, numEdges;//图中当前的顶点数和边数
}MGraph;Boolean visited[MAXVEX];
//邻接矩阵的深度优先递归算法
void DFS(MGraph G, int i) {int j;visited[i] = TRUE;cout << G.vexs[i] << " ";for (j = 0; j < G.numVertexes; j++) {//若两点连通且未被访问过if (G.arc[i][j] == 1 && !visited[j])DFS(G, j);}
}//邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G) {int i;for (i = 0; i < G.numVertexes; i++)visited[i] = FALSE;//初始化顶点状态,所有顶点都是未访问过的状态for (i = 0; i < G.numVertexes; i++)if (!visited[i])//对未访问过的顶点调用DFS,若是连通图,只会执行一次DFS(G, i);
}

邻接表代码

typedef struct EdgeNode {//边表节点int adjvex;//邻接点域,存储该顶点对应的下标EdgeType weight;//用于存储权值,对于非网图可以不需要struct EdgeNode* next;//指向下一个邻接点
}EdgeNode;typedef struct VertexNode {//顶点表节点VertexType data;//顶点域,存储顶点信息EdgeNode* firstedge;//边表头指针
}VertexNode, AdjList[MAXVEX];typedef struct {AdjList adjList;int numVertexes, numEdges;//图中当前的顶点数和边数
}GraphAdjList;void DFS(GraphAdjList GL, int i) {EdgeNode* p;visited[i] = TRUE;cout << GL.adjList[i].data << ' ';p = GL.adjList[i].firstedge;while (p) {if (!visited[p->adjvex])DFS(GL, p->adjvex);//对未访问的邻接顶点递归调用p = p->next;}
}void DFSTraverse(GraphAdjList GL) {int i;for (i = 0; i < GL.numVertexes; i++)visited[i] = FALSE;//初始化所有未访问节点for (i = 0; i < GL.numVertexes; i++)if (!visited[i])//对所有未访问顶点调用DFS,若是连通图,则只会调用一次DFS(GL, i);
}

对比两个不同存储结构的深度优先遍历算法,对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O(n²)的时间。而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O(n+e)。显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。

广度优先遍历

广度优先遍历(Breadth _First _Search), 又称为广度优先搜索,简称BFS
如果说图的深度优先遍历类似树的前序遍历,那么图的广度优先遍历就类似于树的层序遍历。

邻接矩阵

//#define TRUE 1
//#define FALSE 0
//
//typedef int Boolean;
//typedef char VertexType;//顶点类型
//typedef int EdgeType;//边的权值类型
//#define MAXVEX 100//最大顶点数
//#define INFINITY 65535//用65535表示∞
//#define MAXSIZE 20
//#define OK 1
//#define ERROR 0
//
//typedef int QElemType;//数据类型
//typedef int Status;//函数类型
//
//typedef struct Queue {
//	QElemType data[MAXSIZE];
//	int front;//头指针
//	int rear;//尾指针,若队列不空,指向队列尾部元素的下一个位置
//}SqQueue;
//
初始化一个空队列Q
//Status InitQueue(SqQueue* Q) {
//	Q->front = 0;
//	Q->rear = 0;
//	return OK;
//}
//
循环队列的入队操作
若队列未满,则插入元素e为Q新的队尾元素
//Status EnQueue(SqQueue* Q, QElemType e) {
//	if ((Q->rear + 1) % MAXSIZE == Q->front)return ERROR;//队列满了,新元素爬开
//	Q->data[Q->rear] = e;//将元素e赋值给队尾
//	Q->rear = (Q->rear + 1) % MAXSIZE;//rear指针向后移动一位,若到最后则转到数组头部
//	return OK;
//}
//
//Status QueueEmpty(SqQueue Q) {
//	if (Q.rear == Q.front)return TRUE;
//	else return FALSE;
//}
//
循环队列的出队操作
若队列不空,则删除Q中队头元素,用e返回其值
//Status DeQueue(SqQueue* Q, QElemType* e) {
//	if (Q->front == Q->rear)return ERROR;//队伍是空的,憨批
//	*e = Q->data[Q->front];//将队头元素赋值给e
//	Q->front = (Q->front + 1) % MAXSIZE;//front指针向后移动一位,若到最后则转到数组头部
//	return OK;
//}
//
//typedef struct {
//	VertexType vexs[MAXVEX];//顶点表
//	EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵
//	int numVertexes, numEdges;//图中当前的顶点数和边数
//}MGraph;
//
//Boolean visited[MAXVEX];
void BFSTraverse(MGraph G) {int i, j;Queue Q;for (i = 0; i < G.numVertexes; i++)visited[i] = FALSE;InitQueue(&Q);//初始化一个辅助用的队列for (i = 0; i < G.numVertexes; i++) {//对每一个顶点做循环if (!visited[i]) {//若是当前顶点未访问过visited[i] = TRUE;//设置为访问过cout << G.vexs[i] << ' ';EnQueue(&Q, i);//将此顶点入队while (!QueueEmpty(Q)) {//若当前队列不空DeQueue(&Q, &i);//将队中元素出列,赋值给ifor (j = 0; j < G.numVertexes; j++){//判断其他顶点与当前顶点存在边且未访问过if (G.arc[i][j] == 1 && !visited[j]) {//将找到的此顶点标记为已访问visited[j] = TRUE;cout << G.vexs[j] << ' ';EnQueue(&Q, j);//将找到的此顶点入队}}}}}
}

邻接表

懒得写了,先欠着
对比图的深度优先遍历与广度优先遍历算法,你会发现,它们在时间复杂度上是一样的, 不同之处仅仅在于对顶点访问的顺序不同。可见两者在全图遍历上是没有优劣之分的,只是视不同的情况选择不同的算法。

最小生成树

最小生成树很简单,两种方法:

  1. 在图中依次拿出使得现有生成子图无环的权值最小的边即可
  2. 在图中依次去掉可以成环的权值最大的边即可

最短路径算法

dijkstra算法参考这里:https://blog.csdn.net/qq_35644234/article/details/60870719
Floyd算法参考这里:https://blog.csdn.net/ljhandlwt/article/details/52096932
https://blog.csdn.net/jeffleo/article/details/53349825


http://chatgpt.dhexx.cn/article/B9tsZVqc.shtml

相关文章

深度优先遍历和广度优先遍历

深度优先遍历和广度优先遍历 什么是 深度/广度 优先遍历&#xff1f; 深度优先遍历简称DFS&#xff08;Depth First Search&#xff09;&#xff0c;广度优先遍历简称BFS&#xff08;Breadth First Search&#xff09;&#xff0c;它们是遍历图当中所有顶点的两种方式。 这…

图的遍历(深度优先搜索)

1、深度优先搜索遍历过程 图的深度优先搜索(Depth First Search)&#xff0c;和树的先序遍历比较类似。 它的思想&#xff1a;假设初始状态是图中所有顶点均未被访问&#xff0c;则从某个顶点v出发&#xff0c;首先访问该顶点&#xff0c;然后依次从它的各个未被访问的邻接点出…

图的遍历算法之深度优先遍历(DFS)(C++)

图的深度优先遍历思想是&#xff1a; 从图中某结点出发&#xff0c;访问其某一相邻结点&#xff0c;再访问该结点的相邻结点&#xff0c;直至访问完所有的结点。 形象的比喻就是&#xff1a;一条路走到头&#xff0c;回头再走没走过的路。 可见&#xff0c;深度优先遍历是一…

图的深度优先遍历

一 图遍历介绍 所谓图的遍历&#xff0c;即是对结点的访问。一个图有那么多个结点&#xff0c;如何遍历这些结点&#xff0c;需要特定策略&#xff0c;一般有两种访问策略。 1 深度优先遍历 2 广度优先遍历 二 深度优先遍历基本思想 图的深度优先搜索(Depth First Search…

图的遍历 ——深度优先遍历

图的遍历 ——深度优先遍历 深度优先搜索&#xff08;Depth First Search&#xff0c;DFS&#xff09;是最常见的图搜索方法之一。 深度优先搜索沿着一条路径一直搜索下去&#xff0c;在无法搜索时&#xff0c;回退到刚刚访问过的节点。深度优先遍历是按照深度优先搜索的方式…

二、图的遍历——深度优先遍历

深度优先遍历&#xff0c;也有称为深度优先搜索&#xff0c;简称为DFS。 深度优先遍历其实就是一个递归的过程&#xff0c;它从图中某个顶点ⅴ出发&#xff0c;访问此顶点&#xff0c;然后从V的未被访问的邻接点出发深度优先遍历图&#xff0c;直至图中所有和V有路径相通的顶点…

图的遍历(深度优先遍历DFS,广度优先遍历BFS)以及C语言的实现

遍历的定义&#xff1a; 从已给的连通图中某一顶点出发&#xff0c;沿着一些边访遍图中所有的顶点&#xff0c;且使每个顶点仅被访问一次&#xff0c;就叫做图的遍历&#xff0c;它是图的基本运算&#xff0e; 一:深度优先遍历&#xff08;&#xff24;&#xff26;&#xff…

使用SSM框架上传图片

使用SSM框架上传图片 为了大家方便对照,我上传源码到网盘,有兴趣的自取. ps:其中有一个存储数据的网页,我没删除,可以忽略 链接&#xff1a;https://pan.baidu.com/s/1u24E8mUs4K-raoQgx-ae2A 提取码&#xff1a;java 建数据表 CREATE DATABASE my_resource;USE my_resource…

SSM框架简单介绍

一. SSM框架简介及特征 1.SpringMVC Spring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构&#xff0c;从而在使用Spring进行WEB开发时&#xff0c;可…

SSM框架整合详细教程

目录 1. 什么是SSM&#xff1f; 2. SSM整合的时候容器之间如何访问 3. SSM下面的开发步骤 4. SSM整合时候时容易混乱的知识点 1. 什么是SSM&#xff1f; SSM是对三个框架名字的简写&#xff0c;其中第一个S指的是SpringMVC,第二个S指的是Spring&#xff0c;第三个M指的是M…

SSM框架理解

一、作用&#xff1a; 1、 SSM是spingspringMVCmybatis集成的框架。是标准的MVC模式&#xff0c;将整个系统划分为表现层&#xff0c;controller层&#xff0c;service层&#xff0c;DAO层四层。 2、使用spring MVC负责请求的转发和视图管理&#xff0c;spring实现业务对象管理…

SSM框架初探

SSM 框架初探 SSM框架简介对框架的理解为什么使用 SSM 框架 SpringSpringMVCMybatis SSM框架简介 SSM框架一种以java语言作为后端搭建基本语言的应用系统搭建框架。是继SSH(strutsspringhibernate)之后&#xff0c;目前较为主流的Java EE企业级框架&#xff0c;适用于搭建各种大…

SSM框架整合

今天来整合一下SSM三大框架~~ 1、创建一个maven项目 比较简单就不赘述了&#xff0c;创建的时候选择webapp骨架。 用骨架创建的项目&#xff0c;在创建完之后要更新一下web.xml 模板目录&#xff1a;“你的Tomcat安装目录\webapps\ROOT\WEB-INF\web.xml” 2、项目整体结构 按…

SSM框架原理

SSM框架是spring MVC &#xff0c;spring和mybatis框架的整合&#xff0c;是标准的MVC模式&#xff0c;将整个系统划分为表现层&#xff0c;controller层&#xff0c;service层&#xff0c;DAO层四层 使用spring MVC负责请求的转发和视图管理 spring实现业务对象管理&#xff…

SSM框架总结

一&#xff1a;什么是SSM框架&#xff1f; SSM框架是Spring、SpringMVC和MyBatis框架的总结&#xff0c;是比较标准的MVC模式。 标注的SSM框架有四层&#xff1a;dao&#xff08;mapper&#xff09;层、service层、controller层、domain(entity)层。 使用Spring实现业务对象…

SSM框架详解

SSM框架详解 写在前面&#xff1a;当初整理SSM原理时&#xff0c;参考了网上一些前辈的文章&#xff0c;时间久远已经忘记来源&#xff0c;所以文中原理部分如有侵权请联系我删除。 基于SSM框架的仿天猫商城网站电商后台管理系统 本文视频讲解 文章目录 SSM框架详解 一、项…

java ssm框架论文,ssm框架理解

文章简介: SSM框架集简介 spring框架IOC的理解 mybatis框架sqlSessionFactory理解 Tomcat的理解 图解SSM SSM框架常用注解 1.SSM框架集简介 SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的…

SSM框架详细讲解

SSM框架 文章目录 SSM框架&#xff08;白痴都看完都会&#xff09;介绍SSM框架<原理>一、什么是SSM框架&#xff1f; 1.Spring2.Spring MVC3.Mybatis &#xff08;核心是SqlSession&#xff09;二、代码实战 1.创建配置工程2.代码实战&#xff08;查询记录数&#xff0…

SSM三大框架超详细总结(适合你重新回顾)

目录 1.1 概念 1.2 Mybatis优点 1.3 Mybatis架构 1.4 底层原理 1.5 Mybatis缓存 1.6 常见面试题 2.1 概念 2.2 Spring优点 2.3 Spring架构 2.4 控制反转&#xff08;IOC&#xff09; 2.5 DI依赖注入 2.6 底层原理(常见面试题) 8、如何用基于 Java 配置的方式配置 Spring&#…

SSM框架整合思想及步骤

前言 SSM框架即是将SpringMVC框架、Spring框架、MyBatis框架整合使用。以简化在web开发中繁琐、重复的操作&#xff0c;让开发人员的精力专注于业务处理的开发上。 一、SSM框架的思想 ssm框架根据SpringMVC、Spring、MyBatis三者各自的特性及应用场景对其操作的的业务进行了分…