当前位置: 首页 > news >正文

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

文章目录

  • 前言
  • 广度优先搜索是什么
  • 广度优先搜索的实现
  • 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 -> destAdjListNode* 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 都是一个非常有用的工具。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
  • Adobe After Effects的插件--------Shatter 碎片
  • ppt组织结构图怎么增加分支?
  • 低代码开发:数据分析如何快速响应企业需求
  • Cartographer源码理解
  • Vue接入高德地图并实现基本的路线规划功能
  • 软件测试 | APP测试 —— Appium 的环境搭建及工具安装教程
  • STM32 的 RTC(实时时钟)详解
  • 6.4图的应用
  • 简单题26 - 删除有序数组中的重复项(Java)20240917
  • 图像处理与OCR识别的实践经验(1)
  • 阿里部分集团内部中间件简介
  • Qt:实现单例模式
  • 1.1 计算机网络基本概述
  • #if等命令的学习
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【刷算法】求1+2+3+...+n
  • AWS实战 - 利用IAM对S3做访问控制
  • Fabric架构演变之路
  • Java|序列化异常StreamCorruptedException的解决方法
  • JS函数式编程 数组部分风格 ES6版
  • Laravel 中的一个后期静态绑定
  • scala基础语法(二)
  • select2 取值 遍历 设置默认值
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • vue的全局变量和全局拦截请求器
  • 从零开始的无人驾驶 1
  • 大数据与云计算学习:数据分析(二)
  • 解析带emoji和链接的聊天系统消息
  • 开发基于以太坊智能合约的DApp
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 如何利用MongoDB打造TOP榜小程序
  • 微信公众号开发小记——5.python微信红包
  • 一个SAP顾问在美国的这些年
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • 阿里云服务器如何修改远程端口?
  • ​Spring Boot 分片上传文件
  • ​数据结构之初始二叉树(3)
  • #Lua:Lua调用C++生成的DLL库
  • #pragma multi_compile #pragma shader_feature
  • $LayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (2024.6.23)最新版MAVEN的安装和配置教程(超详细)
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (七)Java对象在Hibernate持久化层的状态
  • (算法设计与分析)第一章算法概述-习题
  • (一)Docker基本介绍
  • (一)认识微服务
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .net refrector
  • .net 生成二级域名
  • .NET 药厂业务系统 CPU爆高分析
  • .NET6 开发一个检查某些状态持续多长时间的类
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析