文章目录
- 前言
- 广度优先搜索是什么
- 广度优先搜索的实现
- [BFS 的具体编程实现](#BFS 的具体编程实现)
- 举例:广度优先搜索的具体步骤
-
- 初始状态:
- [步骤 1:加入起点节点 1](#步骤 1:加入起点节点 1)
- [步骤 2:访问队列中的节点 1,加入其邻居节点 2 和 4](#步骤 2:访问队列中的节点 1,加入其邻居节点 2 和 4)
- [步骤 3:访问队列中的节点 2,加入其未访问的邻居节点 3](#步骤 3:访问队列中的节点 2,加入其未访问的邻居节点 3)
- [步骤 4:访问队列中的节点 4,发现它的邻居(节点 1 和 3)都已访问,跳过](#步骤 4:访问队列中的节点 4,发现它的邻居(节点 1 和 3)都已访问,跳过)
- [步骤 5:访问队列中的节点 3,发现它的邻居(节点 2 和 4)都已访问,跳过](#步骤 5:访问队列中的节点 3,发现它的邻居(节点 2 和 4)都已访问,跳过)
- 编程实现BFS广度优先搜索
- 总结
前言
广度优先搜索(Breadth-First Search,简称 BFS)是图论中的一种基础算法,用于遍历或搜索图的节点。与深度优先搜索(DFS)不同,BFS 是一种层次化的搜索方式,优先访问距离起始节点最近的节点,逐层扩展到更远的节点。由于其遍历顺序的特点,BFS 经常被用于解决最短路径问题、图的连通性检查、树的层序遍历等问题。
在算法竞赛如 CSP-J 中,掌握 BFS 能帮助我们高效地解决图论相关的题目,尤其是在寻找最短路径、求解无权图的某些属性时,BFS 是一种非常有效的工具。
广度优先搜索是什么
BFS 是一种图的遍历算法,类似于按层次的方式搜索图。它从起始节点开始,首先访问所有与该节点直接相邻的节点,然后依次访问每个已访问节点的邻接节点,直到遍历完所有节点或找到目标节点。
BFS 的特点是按最短路径进行遍历。在无权图中,BFS 可以保证从起点到某个节点的路径是最短的。它广泛应用于路径搜索、迷宫问题、连通性检查等场景。
广度优先搜索的实现
BFS 的核心思想是使用队列(FIFO,先进先出)来实现按层次的搜索。具体实现步骤如下:
- 初始化:选择一个起始节点,将其标记为已访问并加入队列。
- 遍历:从队列中取出一个节点,检查其所有未访问的邻接节点,将它们标记为已访问并加入队列。
- 重复:不断从队列中取出节点并访问其邻接节点,直到队列为空。
通过这种方式,BFS 能逐层遍历图的节点,确保从起点到任意节点的访问顺序是按最短路径进行的。
BFS 的具体编程实现
在编程中,BFS 通常使用以下数据结构来实现:
- 队列:用于保存当前层次要访问的节点。
- 访问标记数组:用于记录每个节点是否已经访问过,避免重复访问。
- 图的表示方式:通常使用邻接表或邻接矩阵来存储图的结构信息。
BFS 的伪代码步骤如下:
- 将起点加入队列,并将其标记为已访问。
- 当队列不为空时,取出队列的第一个节点,检查该节点所有邻接节点。
- 对于每个未访问的邻接节点,将其标记为已访问,并加入队列。
- 重复该过程,直到队列为空或找到目标节点。
举例:广度优先搜索的具体步骤
我们通过一个简单的例子来演示 BFS 的具体操作步骤。假设有一个无向图,如下:
plaintext
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 语言代码示例
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;
}
代码解释
-
数据结构定义:
AdjListNode
:表示图中每条边的链表节点。AdjList
:表示每个顶点的邻接链表。Graph
:包含邻接表数组和顶点数的图结构。
-
图的初始化:
createNode
:创建链表节点。createGraph
:初始化图,创建邻接表数组。addEdge
:添加无向边,更新源顶点和目标顶点的邻接表。
-
BFS 实现:
- 初始化访问标记数组和队列。
- 从起始节点开始,标记为已访问并入队。
- 逐层访问图的节点,并将未访问的邻接节点入队。
- 打印每个访问到的节点。
-
主函数:
- 创建图,添加边,调用 BFS 函数,并释放内存。
总结
这段代码演示了如何用 C 语言实现广度优先搜索(BFS)算法。通过邻接表存储图,BFS 遍历每个节点,并按层次访问所有邻接节点。掌握 BFS 的实现可以帮助我们高效解决图论中的许多问题,如最短路径查找、连通性检测等。
总结
广度优先搜索(BFS)是一种重要的图遍历算法,它通过队列的方式,按层次访问图的节点,确保遍历顺序是按最短路径进行的。BFS 特别适用于无权图中的最短路径查找、图的连通性检测等问题。在实际编程中,我们利用队列、访问标记数组等简单的数据结构来实现 BFS 的逻辑,并通过例子演示了该算法的逐步执行过程。
掌握 BFS 能让我们更加高效地解决与图相关的各种问题,无论是在竞赛中,还是在实际开发中,BFS 都是一个非常有用的工具。