广度优先遍历(Breath First Search)

内容纲要

广度优先遍历(Breath First Search)

前言

深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,也频繁出现在 leetcode,高频面试题中。

广度优先遍历简介

广度优先遍历

广度优先遍历,指的是从图的一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历每个相邻节点的相邻节点。

上文所述树的广度优先遍历动图如下,每个节点的值即为它们的遍历顺序。所以广度优先遍历也叫层序遍历,先遍历第一层(节点 1),再遍历第二层(节点 2,3,4),第三层(5,6,7,8),第四层(9,10)。

1

深度优先遍历用的是栈,而广度优先遍历要用队列来实现,我们以下图二叉树为例来看看如何用队列来实现广度优先遍历。

2

#define MAXV 50
#define INF 32767
#include <stdio.h>
#define MaxSize 100
#include <stdlib.h>
typedef struct ANode {
    int adjvex;                 //该边的邻接点编号
    struct ANode *nextarc;      //指向下一条边的指针
    int weight;                 //该边的相关信息,如权值(这里用整型表示)
} ArcNode;                      //边结点类型
typedef struct Vnode {
    //InfoType info             //顶点的其他信息
    ArcNode *firstarc;          //指向第一个边结点
} VNode;
typedef struct {
    VNode adjlist[MAXV];        //邻接表的头结点数组
    int n, e;                   //图中的顶点数n和边数e
} AdjGraph ;                    //完整的图邻接表类型
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e) { //创建图的邻接表
    int i, j;
    ArcNode *p;
    G = (AdjGraph *)malloc(sizeof(AdjGraph));

    for(i = 0; i < n; i++) {                                //给邻接表的头元素的指针域置初值
        G->adjlist[i].firstarc = NULL;
    }

    for(i = 0; i < n; i++) {                                //检查邻接表的每个元素
        for(j = n - 1; j >= 0; j--) {
            if(A[i][j] != 0 && A[i][j] != INF) {            //存在一条边
                p = (ArcNode *)malloc(sizeof(ArcNode));     //创建一个结点p
                p->adjvex = j;                              //存放邻接点
                p->weight = A[i][j];                        //存放权
                p->nextarc = G->adjlist[i].firstarc;        //采用头插法插入结点p
                G->adjlist[i].firstarc = p;
            }
        }
    }

    G->n = n;
    G->e = e;
}
void DispAdj(AdjGraph *G) {
    int i;
    ArcNode *p;

    for(i = 0; i < G->n; i++) {
        p = G->adjlist[i].firstarc;
        printf("%3d: ", i);

        while(p != NULL) {
            printf("%d[%d]→", p->adjvex, p->weight);
            p = p->nextarc;
        }

        printf("^\n");
    }
}
void DestroyAdj(AdjGraph *&G) {
    int i;
    ArcNode *pre, *p;

    for(i = 0; i < G->n; i++) {         //扫描所有单链表
        pre = G->adjlist[i].firstarc;   //p指向第i个单链表的头结点

        if(pre != NULL) {
            p = pre->nextarc;

            while(p != NULL) {          //释放第i个单链表的所有结点
                free(pre);
                pre = p;
                p = p->nextarc;
            }

            free(pre);
        }
    }

    free(G);
}
typedef int ElemType;
typedef struct {
    ElemType data[MaxSize];
    int front, rear;        //队首和队尾指针
} SqQueue;
void InitQueue(SqQueue *&q) {
    q = (SqQueue *)malloc(sizeof(SqQueue));
    q->front = q->rear = 0;
}
void DestroyQueue(SqQueue *&q) {
    free(q);
}
bool QueueEmpty(SqQueue *q) {
    return(q->front == q->rear);
}
bool enQueue(SqQueue *&q, ElemType e) {
    if((q->rear + 1) % MaxSize == q->front)   //队满上溢出
        return false;

    q->rear = (q->rear + 1) % MaxSize;
    q->data[q->rear] = e;
    return true;
}
bool deQueue(SqQueue *&q, ElemType &e) {
    if(q->front == q->rear)               //队空下溢出
        return false;

    q->front = (q->front + 1) % MaxSize;
    e = q->data[q->front];
    return true;
}
//广度优先遍历
void BFS(AdjGraph *G, int v) {
    /********** Begin **********/
    int w, i;
    ArcNode *p;
    SqQueue *qu;                            //定义环形队列指针
    InitQueue(qu);                          //初始化队列
    int visited[MAXV];                      //定义顶点访问标志数组

    for(i = 0; i < G->n; i++) visited[i] = 0;     //访问标志数组初始化

    printf("%2d", v);                         //输出被访问顶点的编号
    visited[v] = 1;                             //置已访问标记
    enQueue(qu, v);

    while(!QueueEmpty(qu)) {                //队不空循环
        deQueue(qu, w);                     //出队一个顶点w
        p = G->adjlist[w].firstarc;          //指向w的第一个邻接点

        while(p != NULL) {                  //查找w的所有邻接点
            if(visited[p->adjvex] == 0) {    //若当前邻接点未被访问
                printf("%2d", p->adjvex);      //访问该邻接点
                visited[p->adjvex] = 1;      //置已访问标记
                enQueue(qu, p->adjvex);      //该顶点进队
            }

            p = p->nextarc;                      //找下一个邻接点
        }
    }

    printf("\n");
    /********** End **********/
}

在这里插入图片描述

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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注