【图】(二)图的遍历 - DFS与BFS - C语言

 图相关文章:

1. 图的建立 - 邻接矩阵与邻接表icon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/127552328?spm=1001.2014.3001.55012. 图的遍历 - DFS与BFSicon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/127558368?spm=1001.2014.3001.55013. 顶点度的计算icon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/127558599?spm=1001.2014.3001.55014. 最小生成树 - Prim与Kruskalicon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/127589852?spm=1001.2014.3001.55015. 单源最短路径 - Dijkstra与Bellman-Fordicon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/127630356?spm=1001.2014.3001.55016. 多源最短路径 - Floydicon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/128039852?spm=1001.2014.3001.55017. 拓扑排序AOV网icon-default.png?t=M85Bhttps://blog.csdn.net/m15253053181/article/details/128042358?spm=1001.2014.3001.5501


目录

搜索算法

1 深度优先搜索DFS

1.1 案例引入

1.2 DFS含义

1.3 代码实现

(1)递归实现 - 邻接矩阵

(2)非递归实现 - 邻接矩阵

(3)递归实现 - 邻接表

(4)非递归实现 - 邻接表

2 广度优先搜索BFS

2.1 案例引入

2.2 BFS含义

2.3 代码实现

(1)队列实现 - 邻接矩阵

(2)队列实现 - 邻接表


搜索算法

寻找到某个特定目标与遍历整张图本质上是相同的,只是算法结束的条件不同。因此下面以遍历整张图为例进行讲解。


1 深度优先搜索DFS

1.1 案例引入

我们从一个实例来引入深度优先的算法思想。

首先,这里有一个迷宫,我们要怎么去寻找到出口呢?相信不少人都听说过“右手原则”——一直走右手边,没路了就返回。这种策略,便蕴藏着深度优先搜索的思想。

将上述策略以文字描述:

① 路分叉时,任选一条边深入;

② 无边可走时,后退一步找新边;

 ③ 找到边,从新边继续深入。

注意,在整个流程中,第②步是十分核心的:

② 无边可走时,后退一步找新边;

这其中蕴含着回溯的思想,也是DFS的本质所在。

接下来我们以一个动图来完整展示以下DFS的整体流程:

1.2 DFS含义

从上述案例不难看出DFS的含义,总结如下:

深度优先搜索DFS是利用回溯的思想,将某条路走的很深,这是DFS的实质。

1.3 代码实现

图的DFS类似于树的前序遍历,分为递归与非递归的方法。

其中,非递归实现流程操作如下:

① 起点入栈,标记为已入栈;

② 出栈,访问出栈结点,标记为已访问。再将与出栈结点相邻接的结点(未访问过且未入栈)入栈,标记为已入栈;

③ 重复②,直至所有结点均入栈。

④ 将所有元素逐个出栈,访问。

以下代码均有详细注释,不再赘述。

(1)递归实现 - 邻接矩阵

/* 递归DFS_MG */
 void DFS_MG_Rec(MGraph MG, int vertex)
 {
     int i;
     int *visited = (int *)malloc(sizeof(int) * MG->numV); // 创建访问数组
     for (i = 0; i < MG->numV; i++)                        // 初始化为未访问
         visited[i] = 0;
     printf("DFS of MG(recursive) result:\n");
     DFS_MG_Rec_part(MG, vertex, visited); // 对邻接矩阵进行递归DFS
     printf("\n");
 }
 ​
 /* 递归DFS_MG的递归部分 */
 void DFS_MG_Rec_part(MGraph MG, int vertex, int *visited)
 {
     int i;
     visited[vertex] = 1; // 标记为已访问
     printf("%c ", MG->nameV[vertex]);
     for (i = 0; i < MG->numV; i++) // 对于V的每个邻接点(未访问)递归调用
         if (MG->dis[vertex][i] != -1)
             if (!visited[i])
                 DFS_MG_Rec_part(MG, i, visited);
 }

(2)非递归实现 - 邻接矩阵

 /* 非递归DFS_MG */
 void DFS_MG_nonRec(MGraph MG, int vertex)
 {
     int i;
     int out;
     Stack stack = initSqStack();
     int *visited = (int *)malloc(sizeof(int) * MG->numV); // 创建访问数组
     int *inStack = (int *)malloc(sizeof(int) * MG->numV); // 创建入栈数组(记录顶点是否入过栈)
     for (i = 0; i < MG->numV; i++)                        // 初始化
     {
         visited[i] = 0;
         inStack[i] = 0;
     }
     SqPush(stack, vertex); // 起点入栈
     inStack[vertex] = 1;
     while (stack->top != stack->base) // 栈非空
     {
         out = SqPop(stack); // 出栈
         visited[out] = 1;   // 标记已访问
         printf("%c ", MG->nameV[out]);
         for (i = 0; i < MG->numV; i++) // 将相邻顶点(未访问)压入栈中
         {
             if (MG->dis[out][i] != -1 && !visited[i] && !inStack[i])
             {
                 SqPush(stack, i);
                 inStack[i] = 1;
             }
         }
     }
     printf("\n");
 }

(3)递归实现 - 邻接表

 /* 递归DFS_L */
 void DFS_L_Rec(AdjList L, int vertex)
 {
     int i;
     int *visited = (int *)malloc(sizeof(int) * L->numV); // 创建访问数组
     for (i = 0; i < L->numV; i++)                        // 初始化为未访问
         visited[i] = 0;
     printf("DFS of AdjList(recursive) result:\n");
     DFS_L_Rec_part(L, vertex, visited); // 对邻接矩阵进行递归DFS
     printf("\n");
 }
 ​
 /* 递归DFS_L的递归部分 */
 void DFS_L_Rec_part(AdjList L, int vertex, int *visited)
 {
     int i;
     LGNode node = L->list[vertex].next;
     visited[vertex] = 1; // 标记为已访问
     printf("%c ", L->nameV[vertex]);
     while (node) // 对于V的每个邻接点(未访问)递归调用
     {
         if (!visited[node->v])
             DFS_L_Rec_part(L, node->v, visited);
         node = node->next;
     }
 }

(4)非递归实现 - 邻接表

 /* 非递归DFS_L */
 void DFS_L_nonRec(AdjList L, int vertex)
 {
     int i;
     int out;
     LGNode node = NULL;
     Stack stack = initSqStack();
     int *visited = (int *)malloc(sizeof(int) * L->numV); // 创建访问数组
     int *inStack = (int *)malloc(sizeof(int) * L->numV); // 创建入栈数组(记录顶点是否入过栈)
     for (i = 0; i < L->numV; i++)                        // 初始化
     {
         visited[i] = 0;
         inStack[i] = 0;
     }
     SqPush(stack, vertex); // 起点入栈
     inStack[vertex] = 1;
     while (stack->top != stack->base) // 栈非空
     {
         out = SqPop(stack); // 出栈
         visited[out] = 1;   // 标记已访问
         printf("%c ", L->nameV[out]);
         node = L->list[out].next;
         while (node) // 对于V的每个邻接点(未访问)递归调用
         {
             if (!visited[node->v] && !inStack[node->v])
             {
                 SqPush(stack, node->v);
                 inStack[node->v] = 1;
             }
             node = node->next;
         }
     }
     printf("\n");
 }


2 广度优先搜索BFS

2.1 案例引入

让我们想象一下这个场景:一滴水滴入水面,关注其涟漪的变化。

 显然,水滴落入水面所激起的涟漪会向相邻区域逐渐扩散

这种依次扩散的现象,其实就是广度优先搜索BFS的思想。

 我们再以一张动图来形象地理解一下。

2.2 BFS含义

从上述案例不难看出BFS的含义,总结如下:

广度优先搜索BFS是尽可能地横向上进行搜索,在处理某顶点时,一次性发现其所有相邻顶点,这是BFS是核心思想。

2.3 代码实现

BFS的思想我们已经清楚,具体要怎么实现呢?难点在于这一条:

在处理某顶点时,一次性发现其所有相邻顶点

如何解决这个问题呢?

我们发现,这种访问的特点可以用队列的数据结构完美解决。

以队列方式进行访问,具体操作如下:

① 起点入队列,标记为已入队列。

② 出队,访问出队结点,标记为已访问。将与出队结点相邻接的所有结点(未访问过且未入队列)入队,标记为已入队。

③ 重复②,直至所有结点已入队。

④ 逐个出队,访问。

以下代码均有详细注释,不再赘述。

(1)队列实现 - 邻接矩阵

 /* BFS_MG 队列 */
 void BFS_MG(MGraph MG, int vertex)
 {
     int i;
     int out;
     Queue queue = createQueue();
     int *visited = (int *)malloc(sizeof(int) * MG->numV); // 创建访问数组
     int *inQueue = (int *)malloc(sizeof(int) * MG->numV); // 创建入队数组(记录顶点是否入过队列)
     for (i = 0; i < MG->numV; i++)                        // 初始化
     {
         visited[i] = 0;
         inQueue[i] = 0;
     }
     enqueue(queue, vertex); // 起点入队
     inQueue[vertex] = 1;
     while (queue->rear) // 队列非空
     {
         out = dequeue(queue); // 出队
         visited[out] = 1;     // 标记已访问
         printf("%c ", MG->nameV[out]);
         for (i = 0; i < MG->numV; i++) // 将相邻顶点(未访问)进入队列
         {
             if (MG->dis[out][i] != -1 && !visited[i] && !inQueue[i])
             {
                 enqueue(queue, i);
                 inQueue[i] = 1;
             }
         }
     }
     printf("\n");
 }

(2)队列实现 - 邻接表

 /* BFS_L 队列 */
 void BFS_L(AdjList L, int vertex)
 {
     int i;
     int out;
     LGNode node = NULL;
     Queue queue = createQueue();
     int *visited = (int *)malloc(sizeof(int) * L->numV); // 创建访问数组
     int *inQueue = (int *)malloc(sizeof(int) * L->numV); // 创建入队数组(记录顶点是否入过队列)
     for (i = 0; i < L->numV; i++)                        // 初始化
     {
         visited[i] = 0;
         inQueue[i] = 0;
     }
     enqueue(queue, vertex); // 起点入队
     inQueue[vertex] = 1;
     while (queue->rear) // 队列非空
     {
         out = dequeue(queue); // 出栈
         visited[out] = 1;     // 标记已访问
         printf("%c ", L->nameV[out]);
         node = L->list[out].next;
         while (node) // 对于V的每个邻接点(未访问)递归调用
         {
             if (!visited[node->v] && !inQueue[node->v])
             {
                 enqueue(queue, node->v);
                 inQueue[node->v] = 1;
             }
             node = node->next;
         }
     }
     printf("\n");
 }

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

友人帐_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值