C语言经典算法之广度优先遍历算法

本文详细介绍了广度优先遍历(BFS)算法的代码实现,探讨了其时间复杂度(与节点数和边数有关)和空间复杂度(与节点数相关),并分析了该算法的优缺点,包括在社交网络分析、网络路由等现实场景中的应用。
摘要由CSDN通过智能技术生成

目录

前言

A.建议

B.简介

一 代码实现

二 时空复杂度

A.时间复杂度:

B.空间复杂度:

C.总结:

三 优缺点

A.优点:

B.缺点:

四 现实中的应用


前言

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.时间复杂度:

时间复杂度为O(V + E),其中V是节点数,E是边数。对每个节点和每条边进行访问一次。
每个节点入队和出队一次,每条边的两个节点分别入队一次。


B.空间复杂度:

空间复杂度为O(V),其中V是节点数。需要使用队列来存储待访问的节点,队列中的节点数不会超过图的节点数。使用一个数组或集合来标记节点是否已访问过,占用的空间也与节点数成正比。

C.总结:

广度优先遍历的时间复杂度主要由节点数和边数决定,而空间复杂度则主要受到节点数的影响。在实际应用中,BFS对于稀疏图相对较为高效,因为它会在每一层扩展前完成对当前层的遍历

三 优缺点


A.优点:


最短路径: BFS能够找到起始节点到其他节点的最短路径,因为它按照层级遍历,首次到达目标节点的路径就是最短路径。

连通性检测: BFS可以有效地检测图中的连通分量,判断图的连通性。

最小生成树: 在无权图中,BFS可以用于构建最小生成树。

网络路由: 用于计算网络中节点之间的最短路径,影响路由算法的设计。

图的遍历: 能够以层级的方式遍历图中的节点,使其更易于理解。

B.缺点:


空间复杂度较高: BFS使用队列来存储待访问节点,可能在图较大时占用较多内存,不适用于大规模图的处理。

非最优路径: 对于权重不同的有向图,BFS找到的路径不一定是最优路径,因为它并未考虑边的权重。

不适用于大规模图: 在某些情况下,BFS需要遍历大量节点和边,可能导致性能下降,不适用于大规模图的处理。

不适用于处理带权图: 在有权图中,BFS通常无法找到最短路径,需要使用其他算法如Dijkstra's或A*。

可能存在环的问题: 在无向图中,BFS在检测环时可能需要额外的处理,以防止重复访问节点。

四 现实中的应用


广度优先遍历算法在现实中有多种应用,特别适用于需要按层级展开搜索的问题。以下是一些广度优先遍历在现实中的应用:

社交网络分析: 在社交网络中,广度优先遍历用于查找用户之间的关系,发现社交圈子,推荐朋友或关注。

网络路由: 广度优先遍历用于计算网络中节点之间的最短路径,影响路由算法的设计,确保信息以最短路径传递。

最短路径搜索: 用于查找从起始位置到目标位置的最短路径,例如在地图导航应用中。

图像处理: 广度优先遍历可用于图像分割、对象提取和区域填充,通过像素之间的连接性进行处理。

文件系统和目录遍历: 在文件系统中,广度优先遍历用于按照层级结构遍历文件和目录,确保按照文件夹层级展开。

连通性检测: 用于检测图中的连通分量,判断一个图是否是强连通图,或者找到图中的孤立子图。

电路设计和信号传播: 广度优先遍历可用于电路设计中的信号传播,确保信号以最短路径传递。

人工智能: 在搜索算法中,广度优先遍历可以用于状态空间的搜索,寻找解决问题的最短路径。

网页爬取: 在网络爬虫中,广度优先遍历用于遍历和爬取网站上的链接,确保以层级方式获取页面信息。

 

  • 18
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是C语言实现图的深度优先遍历和广度优先遍历算法的代码: ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 100 // 边表节点 typedef struct ArcNode { int adjvex; // 邻接点的下标 struct ArcNode *next; // 指向下一个邻接点的指针 } ArcNode; // 顶点节点 typedef struct VNode { int data; // 顶点的数据 ArcNode *first; // 指向第一个邻接点的指针 } VNode; // 邻接表图 typedef struct { VNode vexs[MAX_VERTEX_NUM]; // 顶点数组 int vexnum, arcnum; // 顶点数和边数 } ALGraph; // 初始化邻接表图 void InitGraph(ALGraph *G) { int i; G->vexnum = G->arcnum = 0; for (i = 0; i < MAX_VERTEX_NUM; i++) { G->vexs[i].first = NULL; } } // 添加边 void AddArc(ALGraph *G, int v1, int v2) { ArcNode *p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = v2; p->next = G->vexs[v1].first; G->vexs[v1].first = p; G->arcnum++; } // 深度优先遍历 void DFS(ALGraph *G, int v, int *visited) { ArcNode *p; printf("%d ", G->vexs[v].data); visited[v] = 1; p = G->vexs[v].first; while (p) { if (!visited[p->adjvex]) { DFS(G, p->adjvex, visited); } p = p->next; } } // 广度优先遍历 void BFS(ALGraph *G, int v, int *visited) { int queue[MAX_VERTEX_NUM], front = 0, rear = 0; ArcNode *p; printf("%d ", G->vexs[v].data); visited[v] = 1; queue[rear++] = v; while (front != rear) { v = queue[front++]; p = G->vexs[v].first; while (p) { if (!visited[p->adjvex]) { printf("%d ", G->vexs[p->adjvex].data); visited[p->adjvex] = 1; queue[rear++] = p->adjvex; } p = p->next; } } } int main() { ALGraph G; int visited[MAX_VERTEX_NUM] = {0}; int i; InitGraph(&G); G.vexs[0].data = 0; G.vexs[1].data = 1; G.vexs[2].data = 2; G.vexs[3].data = 3; G.vexs[4].data = 4; G.vexs[5].data = 5; AddArc(&G, 0, 1); AddArc(&G, 0, 2); AddArc(&G, 0, 5); AddArc(&G, 1, 0); AddArc(&G, 1, 2); AddArc(&G, 1, 3); AddArc(&G, 2, 0); AddArc(&G, 2, 1); AddArc(&G, 2, 3); AddArc(&G, 2, 4); AddArc(&G, 3, 1); AddArc(&G, 3, 2); AddArc(&G, 3, 4); AddArc(&G, 4, 2); AddArc(&G, 4, 3); AddArc(&G, 4, 5); AddArc(&G, 5, 0); AddArc(&G, 5, 4); printf("DFS: "); DFS(&G, 0, visited); printf("\n"); for (i = 0; i < MAX_VERTEX_NUM; i++) { visited[i] = 0; } printf("BFS: "); BFS(&G, 0, visited); printf("\n"); return 0; } ``` 这里我们通过邻接表来表示图,其中`VNode`是顶点节点,包括了顶点的数据和指向第一个邻接点的指针,`ArcNode`是边表节点,包括了邻接点的下标和指向下一个邻接点的指针。`ALGraph`是邻接表图,包括了顶点数组和顶点数、边数。 `InitGraph`函数用于初始化邻接表图,将顶点数和边数都赋值为0,同时将每个顶点的第一个邻接点指向空。 `AddArc`函数用于添加边,首先分配一个边表节点,将邻接点的下标赋值给它,然后将该节点插入到顶点的邻接点链表的最前面,最后将边数加1。 `DFS`函数是深度优先遍历算法,从第v个顶点开始遍历,首先输出该顶点的数据,然后将该顶点标记为已访问,接着遍历该顶点的每一个邻接点,如果该邻接点没有被访问过,则递归调用DFS函数。 `BFS`函数是广度优先遍历算法,从第v个顶点开始遍历,首先输出该顶点的数据,然后将该顶点标记为已访问,并将其加入到队列中,接着从队列的最前面取出一个顶点,遍历该顶点的每一个邻接点,如果该邻接点没有被访问过,则输出它的数据,标记为已访问,并将其加入到队列的最后面。 最后在`main`函数中,我们创建了一个邻接表图,然后分别调用了DFS函数和BFS函数进行遍历。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JJJ69

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

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

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

打赏作者

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

抵扣说明:

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

余额充值