CSP-J 算法基础 广度优先搜索BFS


前言

广度优先搜索(Breadth-First Search,简称 BFS)是图论中的一种基础算法,用于遍历或搜索图的节点。与深度优先搜索(DFS)不同,BFS 是一种层次化的搜索方式,优先访问距离起始节点最近的节点,逐层扩展到更远的节点。由于其遍历顺序的特点,BFS 经常被用于解决最短路径问题、图的连通性检查、树的层序遍历等问题。

在算法竞赛如 CSP-J 中,掌握 BFS 能帮助我们高效地解决图论相关的题目,尤其是在寻找最短路径、求解无权图的某些属性时,BFS 是一种非常有效的工具。


广度优先搜索是什么

BFS 是一种图的遍历算法,类似于按层次的方式搜索图。它从起始节点开始,首先访问所有与该节点直接相邻的节点,然后依次访问每个已访问节点的邻接节点,直到遍历完所有节点或找到目标节点。

BFS 的特点是按最短路径进行遍历。在无权图中,BFS 可以保证从起点到某个节点的路径是最短的。它广泛应用于路径搜索、迷宫问题、连通性检查等场景。

广度优先搜索的实现

BFS 的核心思想是使用队列(FIFO,先进先出)来实现按层次的搜索。具体实现步骤如下:

  1. 初始化:选择一个起始节点,将其标记为已访问并加入队列。
  2. 遍历:从队列中取出一个节点,检查其所有未访问的邻接节点,将它们标记为已访问并加入队列。
  3. 重复:不断从队列中取出节点并访问其邻接节点,直到队列为空。

通过这种方式,BFS 能逐层遍历图的节点,确保从起点到任意节点的访问顺序是按最短路径进行的。

BFS 的具体编程实现

在编程中,BFS 通常使用以下数据结构来实现:

  • 队列:用于保存当前层次要访问的节点。
  • 访问标记数组:用于记录每个节点是否已经访问过,避免重复访问。
  • 图的表示方式:通常使用邻接表或邻接矩阵来存储图的结构信息。

BFS 的伪代码步骤如下:

  1. 将起点加入队列,并将其标记为已访问。
  2. 当队列不为空时,取出队列的第一个节点,检查该节点所有邻接节点。
  3. 对于每个未访问的邻接节点,将其标记为已访问,并加入队列。
  4. 重复该过程,直到队列为空或找到目标节点。

举例:广度优先搜索的具体步骤

我们通过一个简单的例子来演示 BFS 的具体操作步骤。假设有一个无向图,如下:

    1 —— 2
    |     |
    4 —— 3

我们从节点 1 开始进行广度优先搜索,目标是遍历所有节点。

初始状态:

  • 队列:空
  • 访问标记数组:所有节点未访问
  • 起点:1

步骤 1:加入起点节点 1

  • 队列:[1]
  • 标记:节点 1 标记为已访问

步骤 2:访问队列中的节点 1,加入其邻居节点 2 和 4

  • 队列:[2, 4]
  • 标记:节点 1、2、4 已访问

步骤 3:访问队列中的节点 2,加入其未访问的邻居节点 3

  • 队列:[4, 3]
  • 标记:节点 1、2、3、4 已访问

步骤 4:访问队列中的节点 4,发现它的邻居(节点 1 和 3)都已访问,跳过

  • 队列:[3]
  • 标记:不变

步骤 5:访问队列中的节点 3,发现它的邻居(节点 2 和 4)都已访问,跳过

  • 队列:空
  • 标记:不变

至此,所有节点都已访问,BFS 结束。

编程实现BFS广度优先搜索

下面是一个简单的广度优先搜索(BFS)的 C 语言代码示例。这段代码实现了一个无向图的 BFS 遍历,展示了如何使用邻接表存储图,并进行 BFS 遍历。

C 语言代码示例

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

// 链表节点表示边
typedef struct AdjListNode {
    int dest;  // 邻居顶点
    struct AdjListNode* next;  // 指向下一个邻接节点
} AdjListNode;

// 顶点的邻接表
typedef struct AdjList {
    AdjListNode* head;  // 链表头指针
} AdjList;

// 图结构
typedef struct {
    int numVertices;  // 顶点数
    AdjList* array;  // 邻接表数组
} Graph;

// 创建链表节点
AdjListNode* createNode(int dest) {
    AdjListNode* newNode = (AdjListNode*)malloc(sizeof(AdjListNode));
    newNode->dest = dest;
    newNode->next = NULL;
    return newNode;
}

// 初始化图
Graph* createGraph(int numVertices) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = numVertices;
    graph->array = (AdjList*)malloc(numVertices * sizeof(AdjList));

    for (int i = 0; i < numVertices; i++) {
        graph->array[i].head = NULL;
    }

    return graph;
}

// 添加无向边
void addEdge(Graph* graph, int src, int dest) {
    // 添加 src -> dest
    AdjListNode* newNode = createNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;

    // 添加 dest -> src (无向图)
    newNode = createNode(src);
    newNode->next = graph->array[dest].head;
    graph->array[dest].head = newNode;
}

// 广度优先搜索
void BFS(Graph* graph, int startVertex) {
    // 创建访问标记数组
    int* visited = (int*)malloc(graph->numVertices * sizeof(int));
    for (int i = 0; i < graph->numVertices; i++) {
        visited[i] = 0;  // 初始化为未访问
    }

    // 创建队列
    int* queue = (int*)malloc(graph->numVertices * sizeof(int));
    int front = 0;
    int rear = 0;

    // 标记起始节点为已访问,并将其入队
    visited[startVertex] = 1;
    queue[rear++] = startVertex;

    while (front < rear) {
        // 出队
        int currentVertex = queue[front++];
        printf("访问顶点 %d\n", currentVertex);

        // 遍历所有邻接节点
        AdjListNode* adjList = graph->array[currentVertex].head;
        while (adjList != NULL) {
            int adjVertex = adjList->dest;
            if (!visited[adjVertex]) {
                visited[adjVertex] = 1;  // 标记为已访问
                queue[rear++] = adjVertex;  // 入队
            }
            adjList = adjList->next;
        }
    }

    // 释放内存
    free(visited);
    free(queue);
}

// 主函数
int main() {
    int numVertices = 5;
    Graph* graph = createGraph(numVertices);

    // 添加边
    addEdge(graph, 0, 1);
    addEdge(graph, 0, 4);
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 3);
    addEdge(graph, 1, 4);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 4);

    // 从顶点 0 开始进行 BFS 遍历
    printf("广度优先搜索结果:\n");
    BFS(graph, 0);

    // 释放内存
    for (int i = 0; i < numVertices; i++) {
        AdjListNode* node = graph->array[i].head;
        while (node) {
            AdjListNode* temp = node;
            node = node->next;
            free(temp);
        }
    }
    free(graph->array);
    free(graph);

    return 0;
}

代码解释

  1. 数据结构定义

    • AdjListNode:表示图中每条边的链表节点。
    • AdjList:表示每个顶点的邻接链表。
    • Graph:包含邻接表数组和顶点数的图结构。
  2. 图的初始化

    • createNode:创建链表节点。
    • createGraph:初始化图,创建邻接表数组。
    • addEdge:添加无向边,更新源顶点和目标顶点的邻接表。
  3. BFS 实现

    • 初始化访问标记数组和队列。
    • 从起始节点开始,标记为已访问并入队。
    • 逐层访问图的节点,并将未访问的邻接节点入队。
    • 打印每个访问到的节点。
  4. 主函数

    • 创建图,添加边,调用 BFS 函数,并释放内存。

总结

这段代码演示了如何用 C 语言实现广度优先搜索(BFS)算法。通过邻接表存储图,BFS 遍历每个节点,并按层次访问所有邻接节点。掌握 BFS 的实现可以帮助我们高效解决图论中的许多问题,如最短路径查找、连通性检测等。


总结

广度优先搜索(BFS)是一种重要的图遍历算法,它通过队列的方式,按层次访问图的节点,确保遍历顺序是按最短路径进行的。BFS 特别适用于无权图中的最短路径查找、图的连通性检测等问题。在实际编程中,我们利用队列、访问标记数组等简单的数据结构来实现 BFS 的逻辑,并通过例子演示了该算法的逐步执行过程。

掌握 BFS 能让我们更加高效地解决与图相关的各种问题,无论是在竞赛中,还是在实际开发中,BFS 都是一个非常有用的工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人才程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值