目录
前言
A.建议
1.学习算法最重要的是理解算法的每一步,而不是记住算法。
2.建议读者学习算法的时候,自己手动一步一步地运行算法。
tips:文中的对数均以2为底数
B.简介
图的广度优先遍历(BFS)是一种用于访问和处理图中节点的算法。从起始节点开始,逐层访问节点,先访问离起始节点最近的节点,然后逐层向外扩展。通过队列实现,保证按照广度顺序遍历,用于查找最短路径、连通性检测等。
一 代码实现
#include <stdio.h>
#include <stdlib.h>
// 定义图的最大节点数
#define MAX_NODES 100
// 定义图的邻接表节点
struct Node {
int data;
struct Node* next;
};
// 定义图
struct Graph {
int numNodes;
struct Node* adjacencyList[MAX_NODES];
int visited[MAX_NODES];
};
// 初始化图
void initGraph(struct Graph* graph, int numNodes) {
graph->numNodes = numNodes;
for (int i = 0; i < numNodes; ++i) {
graph->adjacencyList[i] = NULL;
graph->visited[i] = 0;
}
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
// 创建邻接表节点
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = dest;
newNode->next = graph->adjacencyList[src];
graph->adjacencyList[src] = newNode;
}
// 广度优先遍历
void BFS(struct Graph* graph, int startNode) {
// 创建队列用于存储待访问节点
int queue[MAX_NODES];
int front = 0, rear = 0;
// 将起始节点加入队列并标记为已访问
queue[rear++] = startNode;
graph->visited[startNode] = 1;
// 开始遍历
while (front < rear) {
// 出队列
int currentNode = queue[front++];
printf("%d ", currentNode);
// 遍历邻接节点
struct Node* current = graph->adjacencyList[currentNode];
while (current != NULL) {
if (!graph->visited[current->data]) {
// 将未访问的邻接节点加入队列并标记为已访问
queue[rear++] = current->data;
graph->visited[current->data] = 1;
}
current = current->next;
}
}
}
int main() {
// 创建一个图并初始化
struct Graph graph;
initGraph(&graph, 6);
// 添加图的边
addEdge(&graph, 0, 1);
addEdge(&graph, 0, 2);
addEdge(&graph, 1, 3);
addEdge(&graph, 2, 4);
addEdge(&graph, 3, 5);
// 执行广度优先遍历
printf("Breadth-First Traversal: ");
BFS(&graph, 0);
return 0;
}
这个例子中,通过队列实现了广度优先遍历,起始节点首先入队列,然后从队列中出队列并将其未访问的邻接节点入队列,直到队列为空。这样可以确保按照广度优先的顺序遍历图的节点。
二 时空复杂度
A.时间复杂度:
时间复杂度为,其中是节点数,是边数。对每个节点和每条边进行访问一次。
每个节点入队和出队一次,每条边的两个节点分别入队一次。
B.空间复杂度:
空间复杂度为,其中是节点数。需要使用队列来存储待访问的节点,队列中的节点数不会超过图的节点数。使用一个数组或集合来标记节点是否已访问过,占用的空间也与节点数成正比。
C.总结:
广度优先遍历的时间复杂度主要由节点数和边数决定,而空间复杂度则主要受到节点数的影响。在实际应用中,BFS对于稀疏图相对较为高效,因为它会在每一层扩展前完成对当前层的遍历
三 优缺点
A.优点:
最短路径: BFS能够找到起始节点到其他节点的最短路径,因为它按照层级遍历,首次到达目标节点的路径就是最短路径。
连通性检测: BFS可以有效地检测图中的连通分量,判断图的连通性。
最小生成树: 在无权图中,BFS可以用于构建最小生成树。
网络路由: 用于计算网络中节点之间的最短路径,影响路由算法的设计。
图的遍历: 能够以层级的方式遍历图中的节点,使其更易于理解。
B.缺点:
空间复杂度较高: BFS使用队列来存储待访问节点,可能在图较大时占用较多内存,不适用于大规模图的处理。
非最优路径: 对于权重不同的有向图,BFS找到的路径不一定是最优路径,因为它并未考虑边的权重。
不适用于大规模图: 在某些情况下,BFS需要遍历大量节点和边,可能导致性能下降,不适用于大规模图的处理。
不适用于处理带权图: 在有权图中,BFS通常无法找到最短路径,需要使用其他算法如Dijkstra's或A*。
可能存在环的问题: 在无向图中,BFS在检测环时可能需要额外的处理,以防止重复访问节点。
四 现实中的应用
广度优先遍历算法在现实中有多种应用,特别适用于需要按层级展开搜索的问题。以下是一些广度优先遍历在现实中的应用:
社交网络分析: 在社交网络中,广度优先遍历用于查找用户之间的关系,发现社交圈子,推荐朋友或关注。
网络路由: 广度优先遍历用于计算网络中节点之间的最短路径,影响路由算法的设计,确保信息以最短路径传递。
最短路径搜索: 用于查找从起始位置到目标位置的最短路径,例如在地图导航应用中。
图像处理: 广度优先遍历可用于图像分割、对象提取和区域填充,通过像素之间的连接性进行处理。
文件系统和目录遍历: 在文件系统中,广度优先遍历用于按照层级结构遍历文件和目录,确保按照文件夹层级展开。
连通性检测: 用于检测图中的连通分量,判断一个图是否是强连通图,或者找到图中的孤立子图。
电路设计和信号传播: 广度优先遍历可用于电路设计中的信号传播,确保信号以最短路径传递。
人工智能: 在搜索算法中,广度优先遍历可以用于状态空间的搜索,寻找解决问题的最短路径。
网页爬取: 在网络爬虫中,广度优先遍历用于遍历和爬取网站上的链接,确保以层级方式获取页面信息。