【数据结构第 6 章 ④】- 用 C 语言实现图的深度优先搜索遍历和广度优先搜索遍历

目录

一、深度优先搜索

1.1 - 深度优先搜索遍历的过程

1.2 - 深度优先搜索遍历的算法实现

二、广度优先搜索

2.1 - 广度优先搜索遍历的过程

2.2 - 广度优先搜索遍历的算法实现


 

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

然而,图的遍历要比树的遍历复杂得多,因为图的任一顶点都可能和其余的顶点相连接,所以在访问了某个顶点之后,可能沿着某条搜索路径之后,又回到该顶点上。例如,图一 (b) 中所示的无向图 G2,由于图中存在回路,因此在访问了 v1、v2、v3、v4 之后,沿着边 (v4, v1) 又可访问到 v1。为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已被访问过的顶点,为此,设一个辅助数组 isVisited[n],其初始值为 false 或 0,一旦访问了顶点 vi,便置 isVisited[vi] 为 true 或 1。

根据搜索路径的方向,通常有两条遍历图的路径:深度优先搜索广度优先搜索。它们对无向图和有向图都适用。


一、深度优先搜索

1.1 - 深度优先搜索遍历的过程

深度优先搜索(Depth First Search,DFS)遍历类似于树的先序遍历,是树的先序遍历的推广。

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

  1. 从图中某个顶点 v 出发,访问 v。

  2. 找出刚刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。

  3. 返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该结点。

  4. 重复步骤 2 和 3,直至图中所有顶点都被访问过,搜索结束。

以下图 (a) 中所示的无向图 G4 为例,深度优先搜索遍历图的过程如下图 (b) 所示。

图中以带箭头的粗实线表示遍历时的访问路径,以带箭头的虚线表示回溯路径。小圆圈表示已访问的邻接点,大圆圈表示访问的邻接点。

具体过程如下

  1. 从顶点 v1 出发,访问 v1。

  2. 在访问了 v1 之后,选择第一个未被访问的邻接点 v2,访问 v2。以 v2 为新顶点,重复此步,访问 v4、v8、v5。在访问了 v5 之后,由于 v5 的邻接点都已被访问,此步结束。

  3. 搜索从 v5 回到 v8,由于同样的理由,搜索继续回到 v4、v2 直至 v1,此时由于 v1 的另一个邻接点未被访问,则搜索又从 v1 到 v3,再继续进行下去。由此,得到的顶点访问序列为:v1-->v2-->v4-->v8-->v5-->v3-->v6-->v7。

上图 (b) 中所示的所有顶点加上标有实箭头的边,构成一棵以 v1 为根的树,称为深度优先生成树,如下图所示。

1.2 - 深度优先搜索遍历的算法实现

显然,深度优先搜索遍历连通图是一个递归过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组 isVisited[n],其初值为 false,一旦某个顶点被访问,则其相应的分量置为 true。

void _DFS(Graph* pg, VertexType v, bool* isVisited)
{printf("%c-->", v);int pos = GetVertexPos(pg, v);isVisited[pos] = true;
​int adjVexPos = GetFirstAdjVexPos(pg, v);while (adjVexPos != -1){VertexType w = GetVertexVal(pg, adjVexPos);if (isVisited[adjVexPos] == false)_DFS(pg, w, isVisited);
​adjVexPos = GetNextAdjVexPos(pg, v, w);}
}
​
void DFS(Graph* pg, VertexType v)
{assert(pg);bool* isVisited = (bool*)malloc(sizeof(bool) * pg->vSize);assert(isVisited);for (int i = 0; i < pg->vSize; ++i){isVisited[i] = false;}_DFS(pg, v, isVisited);printf("NULL\n");free(isVisited);isVisited = NULL;
}

若是非连通图,上述遍历过程执行之后,图中一定还有顶点未被访问,需要从图中另选一个未被访问的顶点作为起始点,重复上述深度优先搜索过程,直到图中所有顶点均被访问为止。

void DFSTraverse(Graph* pg)
{assert(pg);bool* isVisited = (bool*)malloc(sizeof(bool) * pg->vSize);assert(isVisited);for (int i = 0; i < pg->vSize; ++i){isVisited[i] = false;}for (int i = 0; i < pg->vSize; ++i){if (isVisited[i] == false){_DFS(pg, GetVertexVal(pg, i), isVisited);printf("NULL\n");}}free(isVisited);isVisited = NULL;
}

调用一次 _DFS 函数将遍历一个连通分量,有多少次调用,就说明图中有多少个连通分量


二、广度优先搜索

2.1 - 广度优先搜索遍历的过程

广度优先搜索(Breadth First Search,BFS)遍历类似于树的层次遍历的过程。

广度优先搜索遍历的过程如下:

  1. 从图中某个顶点 v 出发,访问 v。

  2. 依次访问 v 的各个未曾访问过的邻接点。

  3. 分别从这些邻接点出发依次访问它们的邻接点,并使 "先被访问的顶点的邻接点" 先于 "后被访问的顶点的邻接点" 被访问。重复步骤 3,直至图中所有已被访问的邻接点都被访问到。

例如,对上图 (a) 中的无向图 G4 进行广度优先搜索遍历的过程如下图 (c) 所示。

具体过程如下:

  1. 从顶点 v1 出发,访问 v1。

  2. 依次访问 v1 的各个未曾访问过的邻接点 v2 和 v3。

  3. 依次访问 v2 的邻接点 v4 和 v5,以及 v3 的邻接点 v6 和 v7,最后访问 v4 的邻接点 v8。由于这些顶点的邻接点均已被访问,并且图中所有顶点都被访问,由此完成了图的遍历。得到的顶点访问序列为:v1-->v2-->v3-->v4-->v5-->v6-->v7-->v8。

上图 (c) 中所示的所有顶点加上标有实箭头的边,构成一棵以 v1 为根的树,称为广度优先生成树,如下图所示。

2.2 - 广度优先搜索遍历的算法实现

可以看出,广度优先搜索遍历的特点是:尽可能先对横向进行搜索。假设 x 和 y 是两个相继被访问过的顶点,若当前是以 x 为出发点进行搜索,则在访问 x 的所有未曾被访问过的邻接点之后,紧接着是以 y 为出发点进行横向搜索,并对搜索到的 y 的邻接点中尚未被访问的顶点进行访问。也就是说,先访问的顶点其邻接点亦先被访问。为此,算法实现时需引入队列保存已被访问的顶点

和深度优先搜索类似,广度优先搜索在遍历的过程中也需要一个访问标志数组

广度优先搜索遍历连通图

void _BFS(Graph* pg, VertexType v, bool* isVisited)
{printf("%c-->", v);int pos = GetVertexPos(pg, v);isVisited[pos] = true;
​Queue q;QueueInit(&q);QueuePush(&q, pos);while (!QueueEmpty(&q)){pos = QueueFront(&q);QueuePop(&q);v = GetVertexVal(pg, pos);
​int adjVexPos = GetFirstAdjVexPos(pg, v);while (adjVexPos != -1){VertexType w = GetVertexVal(pg, adjVexPos);if (isVisited[adjVexPos] == false){printf("%c-->", w);isVisited[adjVexPos] = true;QueuePush(&q, adjVexPos);}adjVexPos = GetNextAdjVexPos(pg, v, w);}}QueueDestroy(&q);
}
​
void BFS(Graph* pg, VertexType v)
{assert(pg);bool* isVisited = (bool*)malloc(sizeof(bool) * pg->vSize);assert(isVisited);for (int i = 0; i < pg->vSize; ++i){isVisited[i] = false;}_BFS(pg, v, isVisited);printf("NULL\n");free(isVisited);isVisited = NULL;
}

广度优先搜索遍历非连通图算法的实现类似于深度优先搜索遍历非连通图,只需要将 _DFS 函数调用改为 _BFS 函数调用

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

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

相关文章

壹基金瑞金东升社区儿童服务站上演“甜蜜冬日”亲子DIY蛋糕秀

12月9日上午&#xff0c;一场温情满溢的亲子DIY蛋糕活动&#xff0c;在壹基金瑞金东升社区儿童服务站拉开了帷幕&#xff0c;空气里有香甜的奶油味道&#xff0c;浓浓的温馨气息感染着在场的每一个人。 自己动手做的&#xff0c;才有意义&#xff0c;蛋糕DIY是一项很好的亲子活…

油封在机械系统中不可或缺的作用

油封是众多机械系统中的基本组件。但我们到底为什么要使用油封呢?本文旨在阐明油封所发挥的关键作用及其广泛使用背后的原因。 油封的主要作用&#xff1a; 油封的主要功能是充当屏障。它可以防止润滑油从机器和轴承内部的缝隙中泄漏&#xff0c;确保机械部件的平稳运行。同…

Mysql的聚簇索引(聚集索引)和非聚簇索引的区别

MySQL中的索引分为两种主要类型&#xff1a;聚簇索引&#xff08;Clustered Index&#xff09;和非聚簇索引&#xff08;Non-clustered Index&#xff09;。这两种索引的主要区别在于它们如何组织数据和索引的方式。 聚簇索引&#xff08;Clustered Index&#xff09; 聚簇索…

Linux GPIO 应用编程

Linux GPIO 应用编程 嵌入式Linux应用开发中&#xff0c;系统经常会通过GPIO接入按键、各种感应开关等传感器&#xff0c;或控制电源开关、继电器等设备。 要在Linux应用层控制GPIO有两种方式&#xff1a; 通过sysfs控制通过API控制 通过sysfs控制GPIO 为了用户空间运行的…

FineReport 高级教程:深入数据分析与报表设计

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

想做游戏开发,我应该会点啥?

在知乎上&#xff0c;经常能看到类似“如何入门游戏开发”这样的问题&#xff0c;这篇文章&#xff0c;我试着概括性的对游戏开发所需要的技能做一个总结&#xff0c;希望大家对游戏开发能有一个基本的认识~ 游戏开发基础要求高么&#xff1f; 和其他程序猿一样&#xff0c;要…

前后端交互—开发一个完整的服务器

代码下载 初始化 新建 apiServer 文件夹作为项目根目录&#xff0c;并在项目根目录中运行如下的命令&#xff0c;初始化包管理配置文件: npm init -y运行如下的命令&#xff0c;安装 express、cors: npm i express cors在项目根目录中新建 app.js 作为整个项目的入口文件&a…

12V升18V4A同步升压恒压WT3210

12V升18V4A同步升压恒压WT3210 WT3210 是一款高功率密度的全集成同步升压转换器&#xff0c;内部集成的功率MOSFET管导通电阻为上管8mΩ和下管15mΩ。可为便携式系统提供空间小尺寸 解决方案。WT3210具有 2.7V 至 20V 的宽输入电压范围&#xff0c;应用在单节或两节锂电池的便携…

Go语言运行时与自家平台对比后认识

引子 以前就了解Go语言&#xff0c;因为其天生为并发、并行而生&#xff0c;且在语言层面就进行了内秉设计。 总想对比于我们自研的分布式并发、并行平台&#xff0c;以利于得到一些新认识 &#xff1a;&#xff09; Go官网资料 在Go的官网资料提供了很好的资料和知识库 初…

关于嵌入式开发的一些信息汇总:嵌入式C开发人员、嵌入式系统Linux

关于嵌入式开发的一些信息汇总&#xff1a;嵌入式C开发人员、嵌入式系统Linux 1 关于嵌入式 C 开发人员1.1 嵌入式 C 开发人员必须具备的一些基本技能是&#xff1a;1.2 嵌入式C开发的应用案例 2 如何学习用于嵌入式系统的 Linux2.1 如何学习Linux2.1.1 第一步&#xff1a;创建…

漏刻有时百度地图API实战开发(12)(切片工具的使用、添加自定义图层TileLayer)

TileLayer向地图中添加自定义图层 var tileLayer new BMap.TileLayer();tileLayer.getTilesUrl function (tileCoord, zoom) {var x tileCoord.x;var y tileCoord.y;return images/tiles/ zoom /tile- x _ y .png;}var lockMap new BMap.MapType(lock_map, tileLaye…

【MySQL】MySQL 在 Centos 7环境安装教程

文章目录 1.卸载不要的环境2.检查系统安装包3.获取mysql官方yum源4.安装mysql yum 源&#xff0c;对比前后yum源5.安装mysql服务6.查看配置文件和数据存储位置7.启动服务和查看启动服务8.登录9.配置my.cnf 1.卸载不要的环境 先检查是否有mariadb存在 ps ajx |grep mariadb如果…

【StarRocks-1.简介】

一、简介: starRocks起源于开源软件Doris,其相对Doris的社区环境&#xff0c;starRcoks有商业团队维护、快速版本迭代和dockerHub高支持,让我在生产环境中更加偏向于starRocks&#xff0c;而不是拥抱Doris开源社区。StarRocks的版本更新速度、学习文档和论坛都让小白更加容易入…

Spring+Mybatis如何处理大批量sql(for循环+批处理法)

如果只是单纯地将Mapper语句放在循环中&#xff0c;那么会为每次的循环都创建一个事务&#xff0c;导致大批量的sql耗时会很长。 如果我们让在同一个循环中的Mapper语句在一个事务中提交&#xff0c;批处理这些sql&#xff0c;不多次创建事务&#xff0c;那么我们就能极大地改…

js 获取当前href

在JavaScript中&#xff0c;你可以通过window.location.href来获取当前页面的URL。下面是一个简单的例子&#xff1a; var currentURL window.location.href; console.log(currentURL);上述代码获取当前页面的完整URL&#xff0c;并将其存储在currentURL变量中。然后&#xf…

C语言定长数组 变长数组 柔性数组

C语言定长数组 变长数组 柔性数组 文章目录 C语言定长数组 变长数组 柔性数组1. 定长数组2. 变长数组3. 柔性数组3.1 结构体的大小3.2 柔性数组的使用 1. 定长数组 在C99标准之前&#xff0c;C语言在创建数组的时候&#xff0c;数组的大小只能使用常量&#xff0c;常量表达式来…

【EI会议征稿】第三届电子信息工程、大数据与计算机技术国际学术会议(EIBDCT 2024)

第三届电子信息工程、大数据与计算机技术国际学术会议&#xff08;EIBDCT 2024&#xff09; 2024 3rd International Conference on Electronic Information Engineering, Big Data and Computer Technology 第三届电子信息工程、大数据与计算机技术国际学术会议&#xff08;…

小狐狸GPT付费2.4.9弹窗版学习源码介绍

小狐狸GPT付费2.4.9弹窗版学习源码是一套基于GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型的开源代码库&#xff0c;旨在帮助开发者快速构建和训练自己的语言模型。该源码集成了多个先进的自然语言处理技术&#xff0c;包括预训练、微调、对话生成等&am…

云服务领取证书,注册域名,实现nginx服务配置证书

目录 1.登录网址腾讯云 2.腾讯云注册域名 3.实名认证&#xff0c;上传信息 4.域名注册 5.领取证书 6.域名与证书绑定 7.下载证书 8.设置环境 9.域名解析 10. 本地域名解析 11.上传证书到目录 12.nginx配置文件做地址重写到证书域名 13.配置证书conf 14.设置index…

磁盘坏道修复工具-是一款非常方便实用的磁盘坏道修复软件-供大家学习研究参考

1、支持磁盘数据擦除。 2、杜绝因硬盘坏道&#xff0c;而产生个人隐私数据泄露的问题。 3、支持对该磁盘格式化。 下载&#xff1a;https://download.csdn.net/download/weixin_43097956/88625682