数据结构学习记录——图的遍历(深度优先搜索、广度优先搜索、为什么需要两种遍历、图不连通怎么办)

news2024/11/26 14:27:42

目录

深度优先搜索

概念

图解过程

伪代码 

时间复杂度

具体代码(C语言)

广度优先搜索 

概念

图解过程 

伪代码

时间复杂度

具体代码(C语言)

为什么需要两种遍历

图不连通怎么办

连通

路径 

回路 

连通图 

连通分量

强连通

强连通图 

强连通分量 

解决-C语言


深度优先搜索

概念

深度优先搜索(Depth First SearchDFS

在图G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:

首先访问出发点v,并将其标记为已访问过;

然后依次从v出发搜索v的每个邻接点w。

若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。

若此时图中仍有未访问的顶点,则返回去另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。

类似于树的先序遍历,例如要深度优先遍历下面的一个图:

图解过程

具体的一个过程如下:

 

伪代码 

时间复杂度

若有N个顶点、E条边,时间复杂度是:

  • 用邻接表存储图,为O(N+E)
  • 用邻接矩阵存储图,为O(N的平方)

具体代码(C语言)

#define MAX_VERTICES 100

int visited[MAX_VERTICES];

typedef struct Node 
{
    int vertex;
    struct Node* next;
} Node;

// 深度优先遍历图函数
void DFS(Node* graph[], int v) 
{
    visited[v] = 1; // 标记当前顶点为已访问
    printf("%d ", v); // 输出当前访问的顶点

    Node* current = graph[v]; // 获取当前顶点的邻接链表头节点
    while (current != NULL) 
    {
        int adjacentVertex = current->vertex; // 获取当前邻接顶点
        if (!visited[adjacentVertex]) 
        {
            DFS(graph, adjacentVertex); // 递归调用DFS,遍历邻接顶点
        }
        current = current->next; // 继续处理下一个邻接顶点
    }
}

广度优先搜索 

概念

广度优先搜索(Breadth First SearchBFS

它从图中的某个顶点开始遍历,先访问它的所有邻接顶点,然后再逐层遍历下去,直到图中所有顶点都被访问到为止。

就类似于树的层序遍历,一样是通过队列来实现,再取之前那个图:

图解过程 

具体遍历过程:

伪代码

时间复杂度

若有N个顶点、E条边,

  • 用邻接表存储图,时间复杂度为O(N+E)
  • 用邻接矩阵存储图,有O(N的平方)

具体代码(C语言)

#define MAX_VERTICES 100 // 最大顶点数

// 广度优先搜索函数
void BFS(Vertex V, Vertex* adjacencyList[]) 
{
    bool visited[MAX_VERTICES]; // 记录顶点是否已访问
    Queue* Q = createQueue(); // 创建一个队列用于存储待访问的顶点

    // 初始化visited数组为false,表示所有顶点都未被访问
    for (int i = 0; i < MAX_VERTICES; i++) 
    {
        visited[i] = false;
    }

    visited[V] = true; // 标记起始顶点V为已访问
    enqueue(Q, V); // 将起始顶点V入队

    while (!isEmpty(Q))
    {
        V = dequeue(Q); // 从队列中取出一个顶点V

        // 遍历V的每个邻接点W
        Vertex* adjVertex = adjacencyList[V]; // 获取顶点V的邻接点链表头指针
        while (adjVertex != NULL) 
        {
            int W = adjVertex->data; // 获取邻接点W的数据

            if (!visited[W]) 
            {
                visited[W] = true; // 标记顶点W为已访问
                enqueue(Q, W); // 将顶点W入队
            }

            adjVertex = adjVertex->next; // 遍历下一个邻接点
        }
    }
}

Vertex* adjacencyList[]参数表示图的邻接表,以便获取顶点V的邻接点链表头指针。在

内部的while循环中,使用adjVertex指针遍历顶点V的邻接点链表,获取每个邻接点W的数据,并进行相应的操作。

 

为什么需要两种遍历

我们可以用走迷宫的方式来解释为什么需要两种遍历方式。

假设我们有一个迷宫,迷宫可以看作是一个图形数据结构,其中每个房间都是一个节点,房间之间的通道则是边。

深度优先遍历(DFS)

当我们使用深度优先遍历时,我们会选择一条路径,沿着这条路径尽可能远地探索下去,直到无法继续为止,然后返回上一层节点继续探索。

换在迷宫中,就意味着我们会选择一个通道,并一直走到无法再走下去为止,然后返回到上一个房间,继续选择下一个可行的通道进行探索。

这样就会导致,我们会沿着一个路径一直走到底,直到找到解决问题的目标或者无路可走。

DFS的特点是适合深度探索,对于搜索路径较深的问题很有效。

广度优先遍历(BFS)
与DFS不同,广度优先遍历是逐层扩展。

我们从起点开始,首先探索与起点直接相邻的节点,然后再探索与这些节点相邻的节点,以此类推,直到找到解决问题的目标或者遍历完整个图。

在迷宫中,我们先选择起点的所有相邻房间,然后再选择这些房间的所有相邻的房间,以此类推。这样做的结果是,我们会逐层地扩展搜索,确保我们在搜索过程中覆盖了所有可能的路径。

BFS的特点是适合寻找最短路径或者层次遍历问题。

所以,DFS和BFS在处理图形数据结构时具有不同的特点和应用场景。DFS适合深度探索和回溯问题,而BFS适合寻找最短路径或者层次遍历问题。根据具体的问题需求,我们可以选择适合的遍历方式来解决。

图不连通怎么办

连通

如果从V到W存在一条(无向)路径,则称V和W是连通的。

路径 

V到W的路径是一系列顶点{V,V1,V2,...,Vn,W}的集合,其中任一对相邻的顶点间都有图中的边。路径的长度是路径中的边数(如果带权,则是所有边的权重和)。如果V到W之间的所有顶点都不同,则称简单路径

回路 

起点等于终点的路径。

连通图 

图中任意两顶点均连通。

连通分量

无向图的极大连通子图。

  • 极大顶点数:以原来的图为来源,再加1个顶点就不连通了。
  • 极大边数:包含子图中所有顶点相连的所有边。

 

根据上面这个图,我们来判断一下以下是否是其连通分量:

(一)

 

 是其连通分量,因为包含了子图中所有顶点相连的边,而且再加一个顶点就不能连通了,例:

 (二)

是其连通分量 ,与(一)类似。

(三)

不是其连通分量 ,不包含子图中所有顶点相连的边。

(四)

不是其连通分量 ,虽然包含了子图所有顶点相连的边,但是不满足极大顶点数,加入A点之后还是连通的。

强连通

有向图中顶点v和w之间存在双向路径,则称v和w是强连通的。 

强连通图 

有向图中任意两顶点均强连通。

强连通分量 

有向图的极大强连通子图。

强连通图:

强连通分量: 

在图G中,

它的强连通分量是:

解决-C语言

#define MAX_VERTICES 100 // 最大顶点数
// 定义图
typedef struct Graph 
{
    int numVertices;
    Node* adjacencyList[MAX_VERTICES];
    int visited[MAX_VERTICES];
} Graph;

// 遍历所有连通分量
void ListComponents(Graph* graph) 
{
    int i;
    for (i = 0; i < graph->numVertices; ++i) 
    {
        if (!graph->visited[i]) 
        {
            DFS(graph, i);
        }
    }
}

end


学习自:MOOC数据结构——陈越、何钦铭

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

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

相关文章

FreeRTOS(2)----任务管理

一&#xff0c;任务的基本概念 FreeRTOS是一个支持多任务的操作系统&#xff0c;多个任务可以共享一个优先级&#xff0c;当任务configUSE_TIME_SLICING 为 1&#xff0c;则可以使用时间调度的方式共享处理器。 简而言之&#xff0c;freertos任务就是一系列任务的集合。 二&…

day05_Java中的运算符

在Java中提供了丰富的运算符 其按照功能分&#xff1a;算术运算符、赋值运算符、比较运算符、逻辑运算、条件运算符按照操作数个数分&#xff1a;一元运算符&#xff08;单目运算符&#xff09;、二元运算符&#xff08;双目运算符&#xff09;、三元运算符 &#xff08;三目…

python 元类

目录 元类type介绍使用元类创建类直接使用type继承type 类和对象的创建过程元类有什么用使用元类实现一个单例模式 元类type介绍 我们知道在python中一切皆对象&#xff0c;即使是类&#xff0c;也是对象&#xff0c;那么类这个对象是的类是谁呢&#xff1f;那就是元类。 通过…

Autosar NM网络管理机制(三大三小模式切换、NM网络报文的帧格式解析、NM中用到的定时器配置)

网络管理机制 AUTOSAR网络管理机制其实十分简单&#xff0c;概括下来三句话&#xff1a; 需要通信就发网络报文&#xff0c;否则就停发网络报文&#xff0c;所有节点同睡同醒。 下图是网络管理的状态机&#xff1a; 网络管理包括三个模式&#xff1a;Network Mode、Prepare …

【指针初阶 2023516】

#include <stdio.h>int main() {int a 100;int * pa &a;//pa是专门用来存放地址&#xff08;指针&#xff09;的&#xff0c;这里的pa就被称为指针变量char* pc &a;//指针变量在32为平台下是4个字节//指针变量在64为平台下是8个字节//int arr[10];//printf(&q…

计算机图形学-GAMES101-3

一、transformation-变换 &#xff08;1&#xff09;Scale 缩放变换示意图 图形每个像素对应坐标的计算公式 写成缩放矩阵的形式 非均匀缩放 &#xff08;2&#xff09;Reflection Matrix 镜面变换示意图 图形每个像素对应坐标的计算公式 写成镜像矩阵的形式 …

阿里HR:年轻人你把握不住.....

前言 去阿里面试测试工程师&#xff0c;这里面水太深&#xff0c;什么未来规划&#xff0c;职业发展的东西都是虚拟的&#xff0c;作者还太年轻&#xff0c;没有那个经历&#xff0c;把握不住。项目只有几个&#xff0c;开心快乐就行&#xff0c;不PK&#xff0c;文明PK。 很…

文心一言 VS chatgpt (17)-- 算法导论4.1 3~4题

三、在你的计算机上实现最大子数组问题的暴力算法和递归算法。请指出多大的问题规模n0是性能交叉点一一从此之后递归算法将击败暴力算法?然后&#xff0c;修改递归算法的基本情况一一当问题规模小于 n0 时采用暴力算法。修改后&#xff0c;性能交叉点会改变吗? 文心一言&…

进程间通信(匿名管道、命名管道、共享内存)

目录 匿名管道 创建管道---pipe() 命名管道 创建FIFO FIFO 操作 用命名管道实现server&client通信 共享内存 1.创建共享内存函数shmget() 2.获得共享内存地址函数shmat() 3.删除共享内存函数shmdt() 4.共享内存控制函数shmctl() 在Linux下的多个进程间的通信机制…

Unittest单元测试框架之unittest的第三方库paramunittest参数化基础及应用

一、unittest参数化介绍 参数化测试用于当需要使用多组不同的测试数据测试同一个方法的时候 paramunittest是unittest实现参数化的一个专门的模块&#xff0c;可以传入多组参数&#xff0c;自动生成多个用例 实现unittest参数化的方式有多种&#xff0c;如ddt、paramunittest等…

Linux-进程信号

Linux-进程信号 一&#xff0c;信号入门信号的概念生活中的信号技术应用角度的信号使用kill -l 查看信号列表信号的处理方式 二&#xff0c;信号产生通过终端按键产生信号Core Dumpcode dump标志位 通过系统调用向进程发信号killraiseabort 由软件条件产生信号SIGPIPESIGALRM 硬…

三届跻身世界级,长沙国际工程机械展的突破之路

文 | 智能相对论 作者 | 易不二 长沙正在成为全球工程机械企业争相奔赴的产业“盛宴”。 近日完美落幕的第三届长沙国际工程机械展&#xff0c;留下了全球30多个国家、1500余家企业“同台竞技”的精彩亮相&#xff0c;并达成了536亿元的现场交易额。 卡特彼勒、日立、沃尔沃…

2022年蓝桥杯:第十三届蓝桥杯大赛软件赛省赛C/C++大学B组真题(考后回顾,文末附真题链接)

目录 第十三届蓝桥杯大赛软件赛省赛C/C大学B组真题&#xff08;考后回顾&#xff09;试题 A: 九进制转十进制试题 B: 顺子日期试题 C: 刷题统计试题 D: 修剪灌木试题 E: X 进制减法试题 F: 统计子矩阵试题 G: 积木画试题 H: 扫雷试题 I: 李白打酒加强版试题 J: 砍竹子 第十三届…

【JavaStript】

目录 &#x1f437;1. JavaScript 的书写形式 &#x1f6e9;1.1 行内式 &#x1f49b; 1.2 内嵌式 &#x1f357;1.3 外部式 &#x1f37f;2. JavaScricpt 的一些常用语句 &#x1f6f4;2.1 输入&#xff1a;prompt &#x1f47d;2.2 输出&#xff1a;alert &#x1f…

Linux线程间的同步和互斥 进程间传统通信方式 5.16

Linux线程间的同步和互斥 同步&#xff1a;有顺序的考虑 按照约定的顺序相互配合完成一件事情&#xff08;红绿灯&#xff09; {释放 产生 资源1&#xff08;V操作&#xff09;&#xff1b;&#xff1b;申请 资源-1&#xff08;p操作&#xff09;} 信号量代表某一类资源&am…

RocketMQ整理

RocketMQ在阿里云上的商业版本,集成了阿里内部一些更深层次的功能及运维定制。开源版本,功能上略有缺失,但大体上是一样的。 使用Java开发,便于深度定制。最早叫MetaQ。消息吞吐量虽然依然不如Kafka,但是却比RabbitMQ高很多。在阿里内部,RocketMQ集群每天处理的请求数超过…

监控需求来源及主流方案对比

我们从开始了解监控系统来说&#xff0c;首先我们要先了解监控的需求来源&#xff0c;即监控系统都可以用于做什么? 监控需求来源 其实最初的需求很简单&#xff0c;即"系统出问题了我们要能及时感知"。后面随着技术的不断发展&#xff0c;我们对监控系统提出了更…

Linux的超级用户及权限

目录 一:Linux下的两个用户 二&#xff1a;权限 1&#xff1a;目录文件 文件创建的默认权限 2&#xff1a;普通文件 一:Linux下的两个用户 在使用Linux的时候会有两个身份,第一个是普通用户,普通用户在很多方面是受阻的,原因就是权限不够,在这种情况下就有一个超级用户,也…

iOS图片系列一 图片的基本属性

图片在项目的开发中使用频率很高&#xff0c;但是绝大部分都是作为普通的展示或者偶尔需要裁剪&#xff0c;并不需要对图片做什么特别的处理&#xff0c;最近做了一个项目对于图片的需求功能比较多&#xff0c;踩了很多坑的同时也对图片的使用有了更深的理解&#xff0c;整理下…

C++面经:初始化全局变量和未初始化全局变量有什么区别

全局变量初始化而且初始值不为0&#xff0c;那么这样的全局变量是放在内存的.data段的&#xff0c;如果全局变量初始值为0或者未初始化&#xff0c;那么这样的全局变量是放在.bss段的。 考点&#xff1a; 考察C/C语言内存模型&#xff0c;.data&#xff0c;.bss段存放的内容。 …