第六章 图 四、图的广度优先遍历(BFS算法、广度优先生成树、广度优先生成森林)

一、实现步骤

广度优先遍历的实现步骤如下:

1.从图的某一个节点开始,将该节点标记为已经访问过的节点。

2.将该节点加入到队列中。

3.当队列非空时,执行以下操作:

a. 取出队列队首节点,并遍历该节点与之相邻的所有未被访问过的节点,并将这些节点标记为已经访问过的节点。

b. 将遍历到的所有未被访问过的节点加入到队列中。

4.重复步骤 3,直到队列为空为止。

在实现广度优先遍历时,需要使用一个数组来保存节点的访问状态,使用一个队列来保存需要遍历的节点。同时,也可以使用一个映射表来保存节点之间的关系,从而方便查找节点和它们之间的关系。

二、代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 100

// 邻接表的定义
typedef struct _edge_node {
    int adjvex; // 邻接点编号
    struct _edge_node *next; // 下一个邻接点
} EdgeNode;

typedef struct _vertex_node {
    int data; // 节点数据
    EdgeNode *first_edge; // 第一个邻接点
} VertexNode;

VertexNode graph[MAX_VERTEX_NUM]; // 邻接表
int visited[MAX_VERTEX_NUM]; // 访问标记数组
int queue[MAX_VERTEX_NUM]; // 队列
int front = 0, rear = 0; // 队列指针

// 初始化邻接表
void init_graph(int n) {
    for (int i = 0; i < n; i++) {
        graph[i].data = i;
        graph[i].first_edge = NULL;
    }
}

// 添加边
void add_edge(int v1, int v2) {
    EdgeNode *e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = v2;
    e->next = graph[v1].first_edge;
    graph[v1].first_edge = e;
}

// 广度优先遍历
void bfs(int v, int n) {
    visited[v] = 1;
    queue[rear++] = v;
    while (front != rear) {
        int u = queue[front++];
        printf("%d ", u);
        EdgeNode *e = graph[u].first_edge;
        while (e) {
            if (!visited[e->adjvex]) {
                visited[e->adjvex] = 1;
                queue[rear++] = e->adjvex;
            }
            e = e->next;
        }
    }
}

int main() {
    int n, m;
    printf("请输入图的顶点数和边数:\n");
    scanf("%d%d", &n, &m);
    init_graph(n);
    printf("请输入边的信息:\n");
    for (int i = 0; i < m; i++) {
        int v1, v2;
        scanf("%d%d", &v1, &v2);
        add_edge(v1, v2);
        add_edge(v2, v1);
    }
    printf("请输入遍历的起始点:\n");
    int v;
    scanf("%d", &v);
    printf("广度优先遍历结果为:");
    bfs(v, n);
    printf("\n");
    return 0;
}

空间复杂度:最坏情况,辅助队列的大小为V,O(V);

时间复杂度:若采用邻接矩阵存储O();若采用邻接表存储O();

三、手算遍历序列

假设有如下无向图:

我们可以从它的任意结点开始遍历

1、假如我们从结点7开始遍历

2、首先访问7,此时,遍历序列为7

3、访问7的邻接结点为3,4,6,8;此时,遍历序列为7,3,4,6,8

4、访问3的邻接结点,发现都被访问过了,跳过。

5、访问4的邻接结点,发现都被访问过了,跳过。

6、访问6的邻接结点2;此时,遍历序列为7,3,4,6,8,2

7、访问8的邻接结点,发现都被访问过了,跳过。

8、访问2的邻接结点1;此时,遍历序列为7,3,4,6,8,2,1

9、访问1的邻接结点5;此时,遍历序列为7,3,4,6,8,2,1,5

10、没有邻接结点了,遍历辅助队列,查看是否还有为遍历结点,发现还有结点9,10,11

11、重新调用BFS算法,从结点9开始遍历

依此类推,最终得到遍历序列7,3,4,6,8,2,1,5,9,10,11。

结论:对于无向图,调用BFS函数的次数=连通分量数

四、广度优先生成树,广度优先生成森林

一、无向图

书接上回:我们才遍历了一个图,所谓优先生成树,也就是每个结点最先被访问的路径,它们组成起来画出的图。

1、我们首先访问的是结点7,目前没有路径就先不动

2、通过结点7,我们访问了结点3,4,6,8;它们是第一次被访问,我们画出路径;

3、然后由结点6,访问了结点2

4、依此类推,得到最后的图

注意:我这个生成树只是其中的一种,根据存储结构的不同,所得到的树也不同。

邻接矩阵:生成树唯一。

邻接表:生成树不唯一。

由与我们还有一个连通向量,所以再加上下图就是广度优先生成森林

二、有向图

由于有向图只能单向通行所以要多次调用BFS算法。

相关推荐
风影小子5 分钟前
注册登录学生管理系统小项目
算法
黑龙江亿林等保7 分钟前
深入探索哈尔滨二级等保下的负载均衡SLB及其核心算法
运维·算法·负载均衡
lucy1530275107910 分钟前
【青牛科技】GC5931:工业风扇驱动芯片的卓越替代者
人工智能·科技·单片机·嵌入式硬件·算法·机器学习
杜杜的man26 分钟前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang
小沈熬夜秃头中୧⍤⃝43 分钟前
【贪心算法】No.1---贪心算法(1)
算法·贪心算法
木向1 小时前
leetcode92:反转链表||
数据结构·c++·算法·leetcode·链表
阿阿越1 小时前
算法每日练 -- 双指针篇(持续更新中)
数据结构·c++·算法
skaiuijing2 小时前
Sparrow系列拓展篇:对调度层进行抽象并引入IPC机制信号量
c语言·算法·操作系统·调度算法·操作系统内核
Star Patrick2 小时前
算法训练(leetcode)二刷第十九天 | *39. 组合总和、*40. 组合总和 II、*131. 分割回文串
python·算法·leetcode
武子康3 小时前
大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
大数据·人工智能·python·深度学习·算法·机器学习·数据挖掘