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

一、实现步骤

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

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

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

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

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

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

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

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

二、代码

#include 
#include 
#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(V^2);若采用邻接表存储O(V+E);

三、手算遍历序列

假设有如下无向图:

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

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

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,目前没有路径就先不动

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

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

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

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

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

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

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

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

邻接矩阵:生成树唯一。

邻接表:生成树不唯一。

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

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

二、有向图

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

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

你可能感兴趣的:(数据结构学习,算法,宽度优先,数据结构)