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

news/2024/9/18 20:15:37 标签: 算法, 宽度优先, 深度优先, c++, noi, 比赛, 数据结构

文章目录

  • 前言
  • 广度优先搜索是什么
  • 广度优先搜索的实现
  • BFS 的具体编程实现
  • 举例:广度优先搜索的具体步骤
    • 初始状态:
    • 步骤 1:加入起点节点 1
    • 步骤 2:访问队列中的节点 1,加入其邻居节点 2 和 4
    • 步骤 3:访问队列中的节点 2,加入其未访问的邻居节点 3
    • 步骤 4:访问队列中的节点 4,发现它的邻居(节点 1 和 3)都已访问,跳过
    • 步骤 5:访问队列中的节点 3,发现它的邻居(节点 2 和 4)都已访问,跳过
  • 编程实现BFS广度优先搜索
      • C 语言代码示例
      • 代码解释
      • 总结
  • 总结


前言

广度优先搜索(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 都是一个非常有用的工具。


http://www.niftyadmin.cn/n/5664456.html

相关文章

(CS231n课程笔记)深度学习之损失函数详解(SVM loss,Softmax,熵,交叉熵,KL散度)

学完了线性分类&#xff0c;我们要开始对预测结果进行评估&#xff0c;进而优化权重w&#xff0c;提高预测精度&#xff0c;这就要用到损失函数。 损失函数&#xff08;Loss Function&#xff09;是机器学习模型中的一个关键概念&#xff0c;用于衡量模型的预测结果与真实标签…

Linux 调用write()函数后,内核一般多久将数据写入磁盘

在 Linux 中&#xff0c;调用 write() 函数后&#xff0c;内核将数据写入磁盘的时间是不确定的。 这取决于多种因素&#xff1a; 1. 文件系统的缓存机制&#xff1a;为了提高性能&#xff0c;文件系统通常会将数据缓存在内存中&#xff0c;然后在合适的时机批量写入磁盘。…

springboot+security为什么@ControllerAdvice自定义的异常处理没有生效

意外遇到一个无语的bug。项目架构差不多&#xff0c;为什么本项目的ControllerAdvice自定义的异常处理没有生效&#xff0c;其他的就可以。 调试如下&#xff1a; 在捕获异常的位置debug ControllerAdvice 标注的类是否被 Spring 容器正确管理。 很明显&#xff0c;没有。找到…

linux安装solr

Solr Downloads - Apache Solr 直接下载&#xff1a;https://dlcdn.apache.org/solr/solr/9.7.0/solr-9.7.0.tgz 这个包依赖jdk11以上版本 需要jdk1.8版本的&#xff0c;下载Index of /dist/lucene/solr/7.1.0 # 解压 tar -zxvf solr-9.7.0.tgz # 进入启动目录 cd solr-9.7…

隐藏excel单元格数据的两个方法

在Excel中&#xff0c;公式是用来计算数据和结果的非常重要的一部分。但是&#xff0c;有时候您可能希望隐藏公式&#xff0c;以保护其不被他人修改或查看。那么今天小编就来给大家分享隐藏excel单元格数据的方法。 一、使用“隐藏”功能 在Excel中&#xff0c;我们还可以使用…

webGL 综合教程100+【目录】

webGL 综合教程100旨在为开发者提供两大方面的知识信息&#xff1a;&#xff08;1&#xff09;提供详细的每个api知识点的详解 &#xff08;2&#xff09;提供实战的示例&#xff0c;提供源代码。 在这量大系统性的知识下&#xff0c;给用户提供清晰的思路和示例参考&#xff0…

【OceanBase 唠嗑了O】—— 2024.09.21 相约济南

活动时间】 9月21日&#xff08;周六&#xff09;14:00-17:00 【活动地点】&#xff1a; 济南市历下区中国联通软件研究院济南分院 (华能路北) 【报名方式】 扫码添加海报右上方的海榕小姐姐&#xff0c;留言【济南站】

SpringBoot 获取 ApplicationContext

1. 概念 ApplicationContext是什么&#xff1f; 简单来说就是Spring中的容器&#xff0c;可以用来获取容器中的各种bean组件&#xff0c;注册监听事件&#xff0c;加载资源文件等功能 2. 获取ApplicationContext的方式 2.1. 创建工具类 通过此工具类&#xff0c;可以方便的…