数据结构 第六章 图——图的遍历

article/2025/10/28 19:12:58

6.5 图的遍历

在前面我们知道,树是一种非线性结构,为了方便它在计算机中的存储,对树进行遍历使它线性化。
而图同样也是一种非线性结构,但是图又是一种不同于树的多对多结构,所以在前面我们将其转换为了多个一对多的结构来描述它的存储结构。

图的遍历同树类似,也是从某一个顶点出发,按照某种方法对图中所有顶点访问且仅访问一次。图的遍历算法是求解图的连通性问题、拓扑排序和关键路径等算法的基础

因为图的任一顶点都可能和其余的顶点相邻接,为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的顶点

通常有两条遍历的图的路径:深度优先搜索和广度优先搜索。

6.5.1 深度优先搜索

1. 深度优先搜索遍历的过程

深度优先搜索(Depth First Search,DFS)遍历类似于树的先序遍历。我们也可以理解一下深度的概念,也就是树中深度的概念,但这需要图要有跟树相似的形状。何谓深度优先?就是从某个顶点一直往下,先不管与它处于同一层次的其它顶点。

对于一个连通图,深度优先搜索遍历的过程如下:

  1. 从图中某个顶点 v v v出发,访问顶点 v v v
  2. 找到刚访问过的顶点的第一个未被访问的邻接点,接着访问该邻接点;然后以该邻接点为新的顶点,重复上述步骤,直到刚访问过的顶点没有未被访问的邻接点。
  3. 经过了第二步后,由 v v v的第一个未被访问的邻接点延伸的后面的顶点都被访问了(但不确定这条延伸链上每个顶点的其它邻接点是否被访问),此时还停留在“最下面”的那个顶点,这时我们需要返回到前一个访问过的仍有未被访问的邻接点的顶点。若停留顶点的前面一个顶点的邻接点都被访问过,则继续“向上”回溯。
  4. 重复步骤(2)和(3),直至图中所有顶点都被访问过,搜索结束。

下面举一个例子,来说明该搜索方法,如下图:在这里插入图片描述
如果我们要遍历该图,按照上述的步骤进行,那么即:

  1. 从顶点 v 1 v_{1} v1出发,访问 v 1 v_{1} v1(也可从其它任一顶点出发)
  2. 选择 v 1 v_{1} v1未被访问的第一个邻接点 v 2 v_{2} v2(也可以是 v 3 v_{3} v3),访问 v 2 v_{2} v2。又以 v 2 v_{2} v2为新顶点,那么这条延伸链则为: v 1 → v 2 → v 4 → v 8 → v 5 v_{1}\rightarrow v_{2}\rightarrow v_{4}\rightarrow v_{8}\rightarrow v_{5} v1v2v4v8v5,因为 v 5 v_{5} v5的邻接点都已被访问过,最后停留在 v 5 v_{5} v5
  3. 此时我们需要回溯,找到前面顶点的未被访问过的邻接点。 v 5 → v 8 v_5\rightarrow v_{8} v5v8, v 8 → v 4 v_{8}\rightarrow v_{4} v8v4, v 4 → v 2 v_{4}\rightarrow v_{2} v4v2,都没有找到未被访问的邻接点,最后 v 2 → v 1 v_{2}\rightarrow v_{1} v2v1 v 1 v_{1} v1有未被访问的邻接点,搜索又从 v 1 → v 3 v_{1}\rightarrow v_{3} v1v3,继续进行下去。
  4. 最后,我们得到的访问序列为: v 1 → v 2 → v 4 → v 8 → v 5 → v 3 → v 6 → v 7 v_{1}\rightarrow v_{2}\rightarrow v_{4}\rightarrow v_{8}\rightarrow v_{5}\rightarrow v_{3}\rightarrow v_{6}\rightarrow v_{7} v1v2v4v8v5v3v6v7

因为我们遍历是求解图的连通性问题,若访问结束没有未被访问的结点,则是连通图;若有,则是非连通图。

对于上述深度优先搜索的结果,我们可以构造一棵以 v 1 v_{1} v1为根的树,称为深度优先生成树,如下图:在这里插入图片描述

2. 深度优先搜索遍历的算法实现

由于遍历的过程需要判断搜索的顶点是否已被访问,所以我们应该想办法在搜索的时候判断每个是否已被访问。已知,每个顶点只有两种状态:已被访问和未被访问。所以如果该顶点未被访问,只需将其状态改为已被访问就行。而该判断方法比较简单,所以我们可以考虑用一维数组visited来存储每个顶点的状态。若未被访问,则其值为false;若已访问,则其值为true。在这里可以使用布尔类型来实现。 → \rightarrow 布尔数据类型

算法——深度优先搜索遍历连通图

算法分析:

  1. 首先我们将数组visited初始化,将每个顶点的状态值都赋为false
  2. 再从该顶点延伸往下,都是访问的该延伸链中每个顶点的第一个邻接点,最后再从该延伸链最后一个顶点回溯。从这里我们可以看到,要访问一个顶点的全部邻接点,是从最下面的那个顶点开始,逐层向上。而这,不就是一个递归的过程吗。而每个顶点要访问所有邻接点,肯定也是一个循环结构。
  3. 然后我们需要注意的是循环和递归结束的条件。循环结束的条件不用说,肯定是邻接点访问完后。而递归结束的条件就是循环结束,循环结束后向上回溯,直至第一个顶点 v v v的循环结束
  4. 至此,该连通图的所有顶点全部都被访问过

代码实现:

bool visited[MVNum];
void DFS(Graph G,int v)
{printf("%d",&v);    				//打印出每个顶点,方便最后检查是否全部顶点已被访问visited[v]=ture;for(int w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))if(!visited[w])				    //如果该邻接点未被访问DFS(G,w);
}

其中FirstAdjVex和NextAdjVex函数要根据具体的图的存储结构来设计。

算法——深度优先搜索遍历非连通图

算法分析:

  1. 非连通图我们可以理解为是由两个及两个以上的图构成的
  2. 由此,该算法不能向遍历连通图那样,从任一一个顶点出发就可遍历所有顶点。在此算法中,若采用上述算法,则只能遍历“一个图”,其它图就不能遍历
  3. 于是,我们只有对所有顶点做一个循环,只要有未被访问的顶点,就从该顶点出发调用上述算法

代码实现:

void DFSTraverse(Graph G)
{for(int v=0;v<G.vexnum;v++)visited[v]=false;for(int v=0;v<G.vexnum;v++)if(!visited[v])DFS(G,v);

下面分别采用两种不同的图结构来实现深度优先搜索。有了上述过程的理解,我们直接给出代码实现。

算法——采用邻接矩阵表示图的深度优先搜索遍历

void DFS_AM(AMGraph G,int v)
{for(int i=0;i<vexnum;i++)visited[i]=false;printf("%d ",&v);visited[v]=true;for(w=0;w<G.vexnum;w++)if((G.arcs[v][w]!=0)&&!visited[w])DFS_AM(G,w);
}

算法——采用邻接表表示图的深度优先搜索遍历

void DFS_AL(ALGraph G,int v)
{ArcNode *p;for(int i=0;i<G.vexnum;i++)visited[i]=false;printf("%d ",&v);visited[v]=true;p=G.vertices[v].firstarc;while(p!=NUll){w=p->adjvex;if(!visited[w])DFS_AL(G,w);p=p->nextarc;}
}

3. 对深度优先搜索算法的分析

在遍历图时,对图中每个顶点至多调用依次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费时间取决于所采用的存储结构。

  1. 若用邻接矩阵:时间复杂度为 O ( n 2 ) O(n^2) O(n2),n为顶点数
  2. 若用邻接表:时间复杂度为 O ( e ) O(e) O(e),e为边数

6.5.2 广度优先搜索

广度优先搜索和深度优先搜索正好相反,如果说深度优先搜索是“竖”着来,那么广度优先搜索就是“横”着来。

1. 广度优先搜索遍历的过程

该过程类似于树的按层次遍历

  1. 依旧是从图中的某个顶点 v v v出发,并访问 v v v
  2. 依次访问 v v v的所有未被访问过的邻接点
  3. 再从邻接点出发,依次访问它们的邻接点,并使先被访问的顶点的邻接点先于后被访问的顶点的邻接点。重复该步骤,直至图中所有已被访问的顶点的邻接点都被访问到

下图为对无向图G4广度优先搜索遍历的一个示例过程:在这里插入图片描述
在这里插入图片描述
由此我们得到的访问序列为: v 1 → v 2 → v 3 → v 4 → v 5 → v 6 → v 7 → v 8 v_{1}\rightarrow v_{2}\rightarrow v_{3}\rightarrow v_{4}\rightarrow v_{5}\rightarrow v_{6}\rightarrow v_{7}\rightarrow v_{8} v1v2v3v4v5v6v7v8

而上述图也可作为广度优先搜索的一棵广度优先生成树

2. 广度优先搜索遍历的算法实现

算法分析:

  1. 在前面我们说到,在广度优先搜索中要使先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,但前提是先访问了这两个顶点,再去按照一定次序分别访问它们的邻接点
  2. 如果我们把它想象成树的结构那么就好理解了,上一条的意思就是在每一层一定要以从左到右的次序访问顶点
  3. 在这里是一层一层从上到下访问的,所以递归是行不通的,而对访问的次序又有一定的要求。我们再来捋一捋思路:假如从 v 1 v_{1} v1出发,再访问它的邻接点 v 2 , v 3 v_{2},v_{3} v2,v3(先访问的是 v 2 v_{2} v2),然后再访问 v 2 , v 3 v_{2},v_{3} v2,v3的邻接点(先访问的是 v 2 v_{2} v2的邻接点,也就是 v 4 , v 5 v_{4},v_{5} v4,v5,再访问 v 3 v_{3} v3的邻接点也就是, v 6 , v 7 v_{6},v_{7} v6,v7)。由于它们访问有一定的次序,因此按照前面深度优先搜索的算法中某些步骤是很麻烦的
  4. 我们再将它的规则总结以下,也就是先“到”先访问,看到这里是否觉得很熟悉?没错,这就是队列的特点,根据这个规则,我们就可以引入队列,那么就十分方便了,如果队列有点忘了,那么可以复习一下: ~~~~~~~~          → \rightarrow 队列
  5. 于是,我们可以利用出队和进队的方式来进行有先后次序的访问

代码实现:

typedef struct
{QElemType *base;int front;int rear;
}SqQueue;void InitQueue(SqQueue &q)
{base=(QElemType*)malloc(MVNum*sizeof(QElemType));q.front=q.rear=0;
}void EnQueue(SqQueue &q,QElemType e)
{if(q.rear+1)%MVNum=q.front;return;q.base[q.rear]=e;q.rear=(q.rear+1)%MVNum;
}void DeQueue(SqQueue &q,QElemType &e)
{if(q.front==q.rear)return;e=q.base[q.front];q.front=(q.front+1)%MVNum;
}bool visited[MVNum];
void BFS(Graph G,int v)
{for(int i=0;i<G.vexnum;i++)visited[i]=false;printf("%d ",&v);visited[v]=true;Initiate(Q);Enqueue(Q,v);while(!QueueEmpty(Q))			//队列非空{Dequeue(Q,u);				//队头元素置为ufor(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex)if(!visited[w]){printf("%d ",&w);visited[w]=true;Enqueue(Q,w);		}}
}

根据上述算法,每个顶点至多进一次队列。遍历图的过程实质上是通过边找邻接点的过程,因此广度优先搜索遍历图的时间复杂度和深度优先搜索遍历相同。

总结

图的遍历其实并不难,关键是我们知道为什么要去遍历图:是求解图的连通性问题、拓扑排序和关键路径等算法的基础。

遍历图可以从两个方向去遍历,即深度和广度。顾名思义,深度就是从上到下,广度就是从左到右。但深度又不是单纯的从上到下,它是从某个顶点出发,延该顶点的第一个邻接点及该邻接点的第一个邻接点等等继续往下延伸,形成了一条延伸链,直到该延伸链最下面的那个顶点没有未被访问的邻接点,最后再由该延伸链向上回溯。而广度,是要把图看成树状的,一层一层从左到右地去遍历。

对于不同的图的结构,遍历的算法又不同,因此因根据实际情况来操作。

这两个遍历方法并没有好坏之分,只是对于顶点访问的顺序不同。


http://chatgpt.dhexx.cn/article/0dYJ67dn.shtml

相关文章

数据结构——图(图的遍历)

图的遍历 图的遍历深度优先遍历&#xff08;DFS&#xff09;DFS算法效率分析深度优先遍历算法的实现广度优先搜索遍历&#xff08;BFS&#xff09;BFS算法效率分析DFS与BFS算法比较 图的遍历 遍历定义&#xff1a;从已给的连通图中某一顶点出发&#xff0c;沿着一些边访遍图中所…

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

一. 什么是深度优先遍历 深度优先遍历可定义如下&#xff1a;首先访问出发点v&#xff0c;并将其标记为已访问过&#xff1b;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过&#xff0c;则以w为新的出发点继续进行深度优先遍历&#xff0c;直至图中所有和源点v有路径相通的…

图的遍历

图的遍历 介绍 是从图的某一顶点出发&#xff0c;按照某种搜索方式对图中所有顶点访问一次且仅一次。图的遍历可以解决很多搜索问题&#xff0c;在实际中应用非常广泛。图的遍历根据搜索方式的不同&#xff0c;分为广度优先搜索和深度优先搜索。 一.深度优先遍历 1.1介绍 深…

视频和图片合成软件,简单快速合成视频和图片

怎么把小视频和图片合成起来&#xff1f;有简单好上手的教程吗&#xff1f;今天就教大家简单几步&#xff0c;把视频和图片合成为照片视频。先看看用数码大师合成视频和图片的效果截图&#xff1a; 第一步&#xff1a;把图片一次性导入&#xff0c;为照片配上文字 点击“添加…

多张图片怎么合成gif?

多张图片怎么合成gif&#xff1f;gif动图是我们比较熟悉的一种网络聊天表情包&#xff0c;通常都是由图片或者视频转换而来的。很多小伙伴会选择用PS来制作GIF动图&#xff0c;但并不是所有小伙伴都会使用PS&#xff0c;所以一个自动简单&#xff0c;可以将图片或者视频直接转换…

android将图片做成视频播放,如何把图片做成视频【图文教程】

教你怎么将手机拍摄的一张张图片整合转换成视频播放更唯美。下面以直走婚纱mv电子相册为例。 安装好软件&#xff0c;先运行绿化程序&#xff0c;然后再运行软件&#xff0c;详情请参看使用说明txt文档。打开之后&#xff0c;有两个模式供你选择&#xff0c;左边是标准模式、右…

php把图片合成视频,如何把照片做成视频 照片音乐视频制作 并插入几段短视频片段...

如何把照片制作成视频&#xff1f;相信大家都已经有所耳闻了&#xff0c;把平时手机或者相机上拍摄的照片&#xff0c;还有拍摄的视频都可以合起来&#xff0c;再添加背景音乐就成了一个非常有纪念价值的视频了。然而已经不是只有照片和音乐的视频了&#xff0c;整个视频全是照…

视频转图片-人脸识别-合成视频

视频转图片-人脸识别-合成视频 代码&#xff1a; import cv2 import os,sys import numpy as npface_xml cv2.CascadeClassifier(r"C:\Users\lenovo\Desktop\python\jiqixuexi\haarcascade_frontalface_default.xml") eye_xml cv2.CascadeClassifier(r"C:\U…

怎样用计算机合并视频,电脑视频合并软件 , 怎样把多个视频合成为一个

昨天小编在搜梁博的歌曲《表态》的视频时(这首歌小编觉得真的很好听推荐下)在一个剪辑视频发现他将梁博在某综艺唱的歌曲全都剪辑在视频里。这里就运用到了一个剪辑视频知识&#xff0c;合并视频。既然看到小编就准备跟大家伙讲讲这个视频剪辑内容。如何把多个视频合并在一个视…

php合成视频特效,视频合并加转场效果

热爱视频后期剪辑的朋友都会知道&#xff0c;在视频的后期制作过程中&#xff0c;常见操作中就有视频合并这一项&#xff0c;及在视频之间加入转场效果&#xff0c;为影视编辑作品带来绚丽多彩的视觉效果&#xff0c;不禁会使人眼前一亮。接下来给大家介绍一款简易好用的视频编…

php 图片生成视频,图片转化为视频的方法 如何将照片制作成为视频

点击上方的下载地址&#xff0c;然后将软件进行安装。安装完毕后&#xff0c;打开软件在这个界面可以选择软件的比例大小和操作模式&#xff0c;我们选择“4:3”和“全功能模式”。 接着我们将要进行操作的图片导入到软件&#xff0c;点击“导入”后在“打开”界面选中所有的图…

ffmpeg使用(多个帧合成视频)

帧生成视频命令&#xff1a; ffmpeg -threads 2 -y -r 24 -i %05d.jpg output.mp4 视频生成帧命令&#xff08;按帧生成图片&#xff09;&#xff1a; ffmpeg -i checkpoints_dstt_car-turn_result.mp4 chaifen/%06d.png 1、下载ffmpeg安装包 https://github.com/BtbN/FFmpe…

matlab jpg合成gif,用MATLAB将照片合成视频或者GIF图片、以及Photoshop制作GIF图片

用MATLAB将照片合成视频或者GIF图片、以及Photoshop制作GIF图片 一、用MATLAB将照片合成视频(我使用的MATLAB是2015版本的) (1)、你需要需要合成视频的图片。 所有照片放在一个文件夹里面因为是使用Matlab的dir函数读取照片,所以读取时,你要先设置好文件名:图片名称按照“00…

FFmpeg初探——基于FFmpeg的图片合成视频

前言 商家在发布商品的时候&#xff0c;大部分情况下是没有视频的&#xff0c;这样往往会造成商品展示不全等问题&#xff0c;而视频制作又比较麻烦&#xff0c;为了解决此痛点&#xff0c;我们需要提供一键合成视频的功能。 之所以选择 FFmpeg&#xff0c;是因为我们期望后续能…

视频照片合成软件哪个好?快速把手机照片做成视频,简单操作,效果精美!

视频照片合成软件哪个好&#xff1f;怎么把照片合成视频&#xff1f;如何快速把手机照片做成视频&#xff1f; 这是我用数码大师把手机照片合成视频的效果截图&#xff1a; 第一步&#xff1a;快速导入多张照片&#xff0c;为照片配上文字 点击“添加相片”就能快速导入照片…

ffmpeg图片+音频合成视频

命令如下&#xff0c;个人纪录 ffmpeg -framerate 0.05 -f image2 -loop 1 -y -i d:/img/img%d.jpg -i d:/img/gyz.mp3 -s 1080*1920 -r 25 -t 100 d:/img/output.mp4 -framerate 速率&#xff0c;越小每张图片停留时间越长 -loop 循环一遍文件夹内的图片 -i 图片路径&#x…

用php把图片合成视频,图片音乐合成视频 多张图片合成视频|图片合成视频软件...

在网络上我们经常见到的电子相册其本质就是图片音乐合成视频&#xff0c;使用一些图片合成视频软件将多张图片合成视频&#xff0c;外加点炫酷的转场特效&#xff0c;so easy的就能完成了。o(*≧▽≦)ツ 想不想知道具体的操作过程&#xff1f;有兴趣的童鞋可以看看下文的~ 这是…

电脑图片合成视频用什么软件?3分钟快速教程,多张图片做成精美视频!

电脑图片合成视频怎么做&#xff1f;图片视频制作用什么软件好&#xff1f;现在大家的照片或图片很多&#xff0c;其实在电脑上把图片做成视频是非常方便的&#xff0c;还能整理好照片&#xff0c;节省空间&#xff0c;图片/照片视频看起来也更加美观。今天直接用数码大师教大家…

当请求类型是octet-stream时,SpringBoot 如何完成文件上传

一、问题背景 这个问题困扰了我一上午&#xff0c;搜索了很多博客&#xff0c;发现网上的springboot都是使用Multipart来接收文件&#xff0c;而客户端使用的是binary&#xff0c;用二进制流来上传文件的&#xff0c;下面记录一下我的解决历程。 二、基础知识 一个请求的参数…

No converter for [class xxx] Content-Type ‘appliction/octet-stream;charset=UTF-8‘ 的解决办法

报错的类&#xff1a;AbstractMessageConverterMethodProcessor 报错的代码块 报错原因 respose在被传入其他的方法后&#xff0c;其content type 被篡改了&#xff0c;导致与request 的content type 不一致导致的 解决方案一&#xff1a;将方法直接return null; 解决方案二&…