【数据结构第 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 函数调用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法leetcode|92. 反转链表 II(rust重拳出击)

文章目录 92. 反转链表 II&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 92. 反转链表 II&#xff1a; 给你单链表的…

基于itextpdf的java读取和更新pdf表单域字段值功能

基于itextpdf的java读取和更新pdf表单域字段值功能 执行结果为&#xff1a; Hello World! keytopmostSubform[0].Page1[0].qhjc[0] keytopmostSubform[0].Page1[0].qhmc[0] keytopmostSubform[0].Page1[0].cqzh[0] keytopmostSubform[0].Page1[0].fm_year[0] keytopmostSubf…

springboot整合vue,将vue项目整合到springboot项目中

将vue项目打包后&#xff0c;与springboot项目整合。 第一步&#xff0c;使用springboot中的thymeleaf模板引擎 导入依赖 <!-- thymeleaf 模板 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-t…

yolov5单目测距+速度测量+目标跟踪

要在YOLOv5中添加测距和测速功能&#xff0c;您需要了解以下两个部分的原理&#xff1a; 单目测距算法 单目测距是使用单个摄像头来估计场景中物体的距离。常见的单目测距算法包括基于视差的方法&#xff08;如立体匹配&#xff09;和基于深度学习的方法&#xff08;如神经网…

锂电池是什么

锂电池 电工电气百科 文章目录 锂电池前言一、锂电池是什么二、锂电池的类别三、锂电池的作用原理总结前言 锂电池相比其他类型的电池具有许多优点,包括高能量密度、长寿命、低自放电率和较低的内阻等。这些特性使它成为现代电子设备和电动交通工具中首选的能源储存技术。然而…

互联网加竞赛 python+opencv+机器学习车牌识别

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于机器学习的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&#xff0c;适…

热烈庆祝安徽普朗膜技术有限公司参加2024济南生物发酵展

公司自2004年注册成立以来主要业务领域主要有以乳酸、氨基酸、抗生素为主的发酵液的提取分离&#xff1b;醋、酱油发酵产品的产品升级&#xff0c;果汁、茶饮料等天然产物提取的除菌和澄清过滤&#xff1b;低聚木糖、低聚果糖、果葡糖浆、高果糖浆等过滤、纯化、浓缩&#xff1…

java SSM酒店客房管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM酒店客房管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

机器学习的12个基础问题

1.阐述批归一化的意义 算法 1&#xff1a;批归一化变换&#xff0c;在一个 mini-batch 上应用于激活 x。 批归一化是一种用于训练神经网络模型的有效方法。这种方法的目标是对特征进行归一化处理&#xff08;使每层网络的输出都经过激活&#xff09;&#xff0c;得到标准差为 …

Mr. Cappuccino的第66杯咖啡——解决MacOS中终端下的中文乱码问题

解决MacOS中终端下的中文乱码问题 中文乱码问题解决方法 中文乱码问题 解决方法 查看Mac使用的是哪个shell echo $SHELL我这里使用的是zsh&#xff0c;将配置添加到.zshrc配置文件中 vi ~/.zshrc 输入i进入编辑模式 esc退出编辑模式 :wq# UTF-8 export LANGen_US.UTF-8加载配…

Excel中MATCH和INDEX函数的用法详解,以及Vlookup的数组用法

match函数 目的&#xff1a;查询函数&#xff0c;范围单元格中搜索特定的项&#xff0c;然后返回该项在此区域中的相对位置。 For example:让 match 去【隔壁办公室】找【老张】 Match 回复&#xff1a;【老张】坐在【隔壁办公室】第【四】个座位上 公式&#xff1a;【 mat…

驱动框架之_gpio_and_pinctrl-设备树的修改

1&#xff1a;设置设备树中的信息 安装“ Pins_Tool_for_i.MX_Processors_v6_x64.exe ”后运行&#xff0c;打开 IMX6ULL 的配置文件“ MCIMX6Y2xxx08.mex ”&#xff0c;就可以在 GUI 界面中选择引脚&#xff0c; 配置它的功能&#xff0c;这就可以自动生成 Pinctrl 的子节…

Linux查看进程的详细信息

使用top找到进程id使用下面命令查看进程的详细信息 systemctl status 17878

Microsoft visual studio 2013卸载方法

1、问 题 Microsoft visual studio 2013 无法通过【程序与功能】卸载 2、解决方法 使用微软的Microsoft visual studio 2013 专用卸载工具 工具下载链接&#xff1a;https://github.com/Microsoft/VisualStudioUninstaller/releases 或 链接&#xff1a;https://pan.baidu.c…

服务器挖矿木马识别与清理

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算,从而占用主机大量的CPU资源,严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源,一般都会对全网进行无差别扫描,同时利用SSH爆破和漏洞利用等手段攻击主机。部分挖矿木马还具备蠕虫化的特点,在主机被成…

华为ensp-无线小型wlan配置教程

实验拓扑图&#xff1a; 实验平台&#xff1a;ENSP510 实验设备&#xff1a;Centered Cloud、AC6005、AP4030、STA、Cellphone vlan范围划分 vlan 101 : 10.23.101.1/24 vlan 100 : 10.23.100.1/24实验步骤&#xff1a; 一、创建VLAN100、101配置端口类型 [AC1]vlan batch 100…

Python日期范围按旬和整月以及剩余区间拆分

昨天见到了一个比较烧脑的问题&#xff1a; 咋一看可能理解问题比较费劲&#xff0c;可以直接看结果示例&#xff1a; 当然这个结果在原问题上基础上有一定改进&#xff0c;例如将同一天以单个日期的形式展示。 如何解决这个问题呢&#xff1f;大家可以先拿测试用例自己试一下…

Android 移动端编译 cityhash动态库

最近做项目&#xff0c; 硬件端 需要 用 cityhash 编译一个 动态库 提供给移动端使用&#xff0c;l 记录一下 编译过程 city .cpp // // Created by Administrator on 2023/12/12. // // Copyright (c) 2011 Google, Inc. // // Permission is hereby granted, free of charg…

Seata客户端启动流程

自动装配 Springboot启动的时候会将下面这几个类进行自动装配 SeataRestTemplateAutoConfiguration(装载拦截器) 这里会装配 SeataRestTemplateInterceptor (Seata的拦截器) Configuration(proxyBeanMethods false) public class SeataRestTemplateAutoConfiguration {B…

塑料检查井产品设计合理、座盖联合周密,为安装维护带来方便

塑料检查井作为一种新型的检查井材料&#xff0c;其产品设计合理、座盖联合周密&#xff0c;为安装维护带来了极大的方便。 首先&#xff0c;塑料检查井的设计合理&#xff0c;能够满足各种工程需求。其结构紧凑、尺寸精确&#xff0c;可以方便地与管道和其他设施进行连接和安…