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


    一、实现步骤

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

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

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

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

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

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

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

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

    二、代码

    1. #include
    2. #include
    3. #define MAX_VERTEX_NUM 100
    4. // 邻接表的定义
    5. typedef struct _edge_node {
    6. int adjvex; // 邻接点编号
    7. struct _edge_node *next; // 下一个邻接点
    8. } EdgeNode;
    9. typedef struct _vertex_node {
    10. int data; // 节点数据
    11. EdgeNode *first_edge; // 第一个邻接点
    12. } VertexNode;
    13. VertexNode graph[MAX_VERTEX_NUM]; // 邻接表
    14. int visited[MAX_VERTEX_NUM]; // 访问标记数组
    15. int queue[MAX_VERTEX_NUM]; // 队列
    16. int front = 0, rear = 0; // 队列指针
    17. // 初始化邻接表
    18. void init_graph(int n) {
    19. for (int i = 0; i < n; i++) {
    20. graph[i].data = i;
    21. graph[i].first_edge = NULL;
    22. }
    23. }
    24. // 添加边
    25. void add_edge(int v1, int v2) {
    26. EdgeNode *e = (EdgeNode*)malloc(sizeof(EdgeNode));
    27. e->adjvex = v2;
    28. e->next = graph[v1].first_edge;
    29. graph[v1].first_edge = e;
    30. }
    31. // 广度优先遍历
    32. void bfs(int v, int n) {
    33. visited[v] = 1;
    34. queue[rear++] = v;
    35. while (front != rear) {
    36. int u = queue[front++];
    37. printf("%d ", u);
    38. EdgeNode *e = graph[u].first_edge;
    39. while (e) {
    40. if (!visited[e->adjvex]) {
    41. visited[e->adjvex] = 1;
    42. queue[rear++] = e->adjvex;
    43. }
    44. e = e->next;
    45. }
    46. }
    47. }
    48. int main() {
    49. int n, m;
    50. printf("请输入图的顶点数和边数:\n");
    51. scanf("%d%d", &n, &m);
    52. init_graph(n);
    53. printf("请输入边的信息:\n");
    54. for (int i = 0; i < m; i++) {
    55. int v1, v2;
    56. scanf("%d%d", &v1, &v2);
    57. add_edge(v1, v2);
    58. add_edge(v2, v1);
    59. }
    60. printf("请输入遍历的起始点:\n");
    61. int v;
    62. scanf("%d", &v);
    63. printf("广度优先遍历结果为:");
    64. bfs(v, n);
    65. printf("\n");
    66. return 0;
    67. }

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

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

    三、手算遍历序列

    假设有如下无向图:

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

    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算法。

  • 相关阅读:
    中国储运杂志中国储运杂志社中国储运编辑部2022年第7期目录
    品牌发展,为什么要做好低价管控
    Epoch、批量大小、迭代次数
    如何打开html格式文件?Win11打开html文件的方法
    部署Envoy
    科技企业如何做到FTP数据安全保护
    浅谈RPC
    华为数通方向HCIP-DataCom H12-831题库(单选题:201-220)
    P8842 [传智杯 #4 初赛] 小卡与质数2 垃圾筛
    【管理运筹学】背诵手册(三)| 运输问题
  • 原文地址:https://blog.csdn.net/icbbm/article/details/132783586