利用邻接矩阵实现连通图的广度优先遍历

利用邻接矩阵实现连通图的广度优先遍历

_

图的世界就像是一个错综复杂的迷宫,而广度优先遍历(BFS)就像是我们手中的一盏灯,带领我们一步一步去探索这个迷宫的每一个角落。通过 C 语言实现一个简单的图遍历算法——广度优先遍历。

一、图与邻接矩阵——图的“连接地图”

在谈论广度优先遍历之前,我们先得了解图的表示方法。图是由节点(顶点)和连接这些节点的边组成的。想象一下,图就像是一个城市地图,节点是城市中的各个地点,而边就是城市中各个地点之间的道路。

为了让计算机理解这种“连接关系”,我们可以用邻接矩阵来表示图。邻接矩阵就是一个二维数组,它记录了图中每两个节点之间是否有边相连。

  • 如果两个节点 iii 和 jjj 之间有边,邻接矩阵中的值就是 1;

  • 如果没有边,值就是 0。

就像在城市地图上,你能清楚地看到哪些地点通过道路连接,哪些地点没有直接的通道。

二、广度优先遍历——一步步的探索

广度优先遍历(BFS)是一种非常直观的遍历方式。它就像是你从某个地方开始,先探访附近的地方,然后逐步扩展,最后覆盖整个区域。具体来说,广度优先遍历的步骤如下:

  1. 从一个起始节点出发,首先访问它。

  2. 将它的所有未访问过的邻接节点加入队列。

  3. 依次从队列中取出节点,访问它,并将它的未访问邻接节点加入队列。

  4. 重复这个过程,直到队列为空。

这个算法特别适合在我们需要“层层扩展”的时候使用,能够确保我们按层次访问每一个节点。

三、用 C 语言实现广度优先遍历

让我们通过 C 语言的代码来实现这个算法。为了让图更加清晰,我们将用邻接矩阵来表示图,并用队列来帮助我们实现广度优先遍历。

1. 定义图的结构

首先,我们需要定义一个图的结构体。图中有一个邻接矩阵,记录了每两个节点之间的连接关系。

#include <stdio.h>
#include <stdlib.h>

#define MAX_VERTICES 50 // 最大顶点数

// 图的结构体
typedef struct {
    int adj[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵
    int numVertices; // 顶点数
} Graph;
2. 初始化图

然后,我们需要一个函数来初始化图,设置邻接矩阵的所有元素为 0,表示最开始没有任何连接。

// 初始化图
void initGraph(Graph* g, int vertices) {
    g->numVertices = vertices;
    
    // 初始化邻接矩阵,全部设置为0
    for (int i = 0; i < vertices; i++) {
        for (int j = 0; j < vertices; j++) {
            g->adj[i][j] = 0;
        }
    }
}
3. 添加边

接下来,编写一个函数来添加边。这个函数会将两点之间的连接标记为 1,表示这两点之间有一条边。

// 添加边
void addEdge(Graph* g, int start, int end) {
    g->adj[start][end] = 1;
    g->adj[end][start] = 1; // 无向图
}
4. 广度优先遍历——探访每一个节点

广度优先遍历的核心是使用队列。我们会将起始节点加入队列,然后不断从队列中取出节点,访问它,并将它的邻接节点加入队列。这个过程就像是一步步向外扩展,直到整个图都被访问一遍。

// 队列结构
typedef struct {
    int items[MAX_VERTICES];
    int front;
    int rear;
} Queue;

// 初始化队列
void initQueue(Queue* q) {
    q->front = -1;
    q->rear = -1;
}

// 判断队列是否为空
int isEmpty(Queue* q) {
    return q->front == -1;
}

// 入队
void enqueue(Queue* q, int value) {
    if (q->rear == MAX_VERTICES - 1) {
        printf("队列已满\n");
        return;
    }
    if (q->front == -1) {
        q->front = 0;
    }
    q->rear++;
    q->items[q->rear] = value;
}

// 出队
int dequeue(Queue* q) {
    if (isEmpty(q)) {
        printf("队列为空\n");
        return -1;
    }
    int item = q->items[q->front];
    q->front++;
    if (q->front > q->rear) {
        q->front = q->rear = -1;
    }
    return item;
}

// 广度优先遍历
void bfs(Graph* g, int start) {
    int visited[MAX_VERTICES] = {0}; // 记录访问情况
    Queue q;
    initQueue(&q);
    
    // 从起始节点开始
    visited[start] = 1;
    printf("从节点 %d 开始广度优先遍历: ", start);
    printf("%d ", start);
    enqueue(&q, start);
    
    while (!isEmpty(&q)) {
        int current = dequeue(&q);
        
        // 遍历当前节点的所有邻接节点
        for (int i = 0; i < g->numVertices; i++) {
            if (g->adj[current][i] == 1 && !visited[i]) {
                printf("%d ", i);
                visited[i] = 1;
                enqueue(&q, i);
            }
        }
    }
    printf("\n");
}
5. 主函数——测试我们的广度优先遍历

最后,编写主函数,初始化一个图,添加一些边,然后从某个节点开始执行广度优先遍历。

int main() {
    Graph g;
    int vertices = 6; // 假设图有6个顶点
    initGraph(&g, vertices);
    
    // 添加一些边
    addEdge(&g, 0, 1);
    addEdge(&g, 0, 2);
    addEdge(&g, 1, 3);
    addEdge(&g, 1, 4);
    addEdge(&g, 3, 5);
    
    // 从节点0开始广度优先遍历
    bfs(&g, 0);
    
    return 0;
}

四、运行结果

假设我们创建的图如下所示:

0 - 1 - 3 - 5
|   |
2   4

从节点 0 开始进行广度优先遍历,输出的结果应该是:

从节点 0 开始广度优先遍历: 0 1 2 3 4 5

五、总结

通过 C 语言实现了一个简单的广度优先遍历算法。广度优先遍历让我们能够从一个起始节点开始,逐层向外扩展,遍历整个图。通过邻接矩阵存储图的结构,我们可以快速检查图中任意两个节点之间是否有边相连。

这个算法虽然看起来简单,但它在很多实际应用中非常有用,比如找最短路径、网络流量分析等。

服务器内存占用过高问题解决分享 2025-12-10
对称加密算法详解 2025-12-21