当前位置:   article > 正文

学数据结构必看!图的深度优先搜索DFS,广度优先搜索BFS算法c语言实现,超详细注释,小白也能懂。_c语言图广度优先

c语言图广度优先

首先介绍一下两个算法是怎样运行的。

深度优先遍历(DFS): 深度优先遍历是一种递归的算法,它从图的某个节点开始,先访问该节点,再递归地访问该节点的相邻节点。深度优先遍历的核心思想是“先入后出”,即先访问到一个节点,就把该节点的所有相邻节点都压入一个栈中,然后再按照“后进先出”的原则依次访问栈中的节点,直到遍历完所有节点。

广度优先遍历(BFS): 广度优先遍历是一种基于队列实现的算法,它从图的某个节点开始,先访问该节点,再将该节点的所有相邻节点加入节点队列中。接着从队列头部取出一个节点,访问该节点,并将该节点的所有相邻节点加入队列中,重复上述过程,直到遍历完所有节点。

对于首次接触图的小白来说,一整套可以运行的,解析详细的代码是十分必要的, 下面提供一个整套的可运行的代码,附带详细的注释,希望你能看懂。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MaxV 100
  4. //边节点
  5. typedef struct Arc
  6. {
  7. int adjvex; //邻接边所指向的顶点
  8. struct Arc* next; //指向下一个邻接边
  9. int weight; //边的权重信息(方便后面最短路径和最小生成树的算法执行)
  10. } Arc;
  11. //顶点节点
  12. typedef struct Vex
  13. {
  14. int data; //顶点的编号
  15. struct Arc* firstArc; //指向第一条邻接的边
  16. } Vex, AdjList[MaxV]; //定义这个结构体叫Vex,一个元素为这个结构体,长度为MaxV的数组叫AdjList.
  17. //图节点
  18. typedef struct Graph
  19. {
  20. AdjList vlist; //邻接表
  21. int vexnum, arcnum; //图的顶点数和边数
  22. } Graph;
  23. int visited[MaxV]; // 用于记录顶点是否被访问过
  24. // 打印图的节点
  25. void print(Graph g)
  26. {
  27. for (int i = 0; i < g.vexnum; i++)
  28. {
  29. printf("%d: ", g.vlist[i].data);
  30. Arc* currentArc = g.vlist[i].firstArc;
  31. while (currentArc != NULL)
  32. {
  33. printf("%d ", currentArc->adjvex);
  34. currentArc = currentArc->next;
  35. }
  36. printf("\n");
  37. }
  38. }
  39. //访问函数
  40. void visit(int v){
  41. visited[v] = 1;
  42. printf("%d ",v);
  43. }
  44. void ManualInit(Graph* g)
  45. {
  46. int vexnum, arcnum;
  47. scanf("%d %d", &vexnum, &arcnum);
  48. g->vexnum = vexnum;
  49. g->arcnum = arcnum;
  50. // 初始化顶点,在这里为了方便,顶点编号和数组下标一致
  51. for (int i = 0; i < vexnum; i++)
  52. {
  53. g->vlist[i].data = i;
  54. g->vlist[i].firstArc = NULL; // 初始化第一个邻接边为NULL
  55. }
  56. // 初始化边
  57. for (int i = 0; i < arcnum; i++)
  58. {
  59. int v1, v2;
  60. scanf("%d %d", &v1, &v2);
  61. // 创建新的边节点
  62. Arc* a1 = (Arc*)malloc(sizeof(Arc));
  63. a1->adjvex = v2;
  64. a1->next = g->vlist[v1].firstArc; // 添加到v1的邻接链表头部
  65. g->vlist[v1].firstArc = a1;
  66. // 如果是无向图,还需要创建一条反向的边
  67. Arc* a2 = (Arc*)malloc(sizeof(Arc));
  68. a2->adjvex = v1;
  69. a2->next = g->vlist[v2].firstArc; // 添加到v2的邻接链表头部
  70. g->vlist[v2].firstArc = a2;
  71. }
  72. }
  73. void autoInit(Graph *g){
  74. int vexnum = 8, arcnum = 10;
  75. g->arcnum = arcnum;
  76. g->vexnum = vexnum;
  77. // 初始化顶点,在这里为了方便,顶点编号和数组下标一致
  78. for (int i = 0; i < vexnum; i++)
  79. {
  80. g->vlist[i].data = i;
  81. g->vlist[i].firstArc = NULL; // 初始化第一个邻接边为NULL
  82. }
  83. //边信息数组
  84. int arc[][2] = {{1,2},{1,3},{1,4},{2,5},{2,4},{3,6},{3,7},{3,0},{5,7},{7,0}};
  85. for (int i = 0; i < arcnum; i++)
  86. {
  87. int v1 = arc[i][0], v2 = arc[i][1];
  88. // 创建新的边节点
  89. Arc* a1 = (Arc*)malloc(sizeof(Arc));
  90. a1->adjvex = v2;
  91. a1->next = g->vlist[v1].firstArc; // 添加到v1的邻接链表头部
  92. g->vlist[v1].firstArc = a1;
  93. // 如果是无向图,还需要创建一条反向的边
  94. Arc* a2 = (Arc*)malloc(sizeof(Arc));
  95. a2->adjvex = v1;
  96. a2->next = g->vlist[v2].firstArc; // 添加到v2的邻接链表头部
  97. g->vlist[v2].firstArc = a2;
  98. }
  99. }
  100. //初始化访问数组
  101. void initVisited(){
  102. for(int i = 0; i < MaxV; i++){
  103. visited[i] = 0;
  104. }
  105. }
  106. // 使用递归实现BFS
  107. void DFS2(Graph g, int start){
  108. //将起始节点的访问状态设置为1
  109. visited[g.vlist[start].data] = 1;
  110. //打印节点
  111. printf("%d ",start);
  112. //获得第一个邻接边
  113. Arc * currentarc = g.vlist[start].firstArc;
  114. //遍历所有的邻接边
  115. while(currentarc != NULL){
  116. //获取邻接的顶点
  117. int adjvex = currentarc->adjvex;
  118. //判断其是否在前面的过程中访问过
  119. if(visited[adjvex] == 0){
  120. //如果未访问则递归函数,从这个点开始深度向下搜索
  121. DFS2(g, adjvex);
  122. }
  123. //邻接边的更新
  124. currentarc = currentarc->next;
  125. }
  126. }
  127. void DFSTravers(Graph g){
  128. //非连通图的深度遍历
  129. for(int i = 0; i < g.vexnum; i++){
  130. //从每个顶点都出发深搜一次
  131. if(visited[i] == 0)
  132. DFS2(g, i);
  133. }
  134. }
  135. int BFS2(Graph g){
  136. initVisited();
  137. //使用数组实现队列
  138. int queue[MaxV];
  139. int front = 0, rear = 0;
  140. //添加一个循环,保证非连通的图也可遍历完
  141. for(int i = 0; i < g.vexnum; i++){
  142. //判断节点是否访问过,如果未访问,添加到队列待访问。
  143. if(visited[i] == 0){
  144. queue[rear++] = i;
  145. }
  146. //如果队列不空就一直访问,队列为空结束循环说明连通的节点已经全部访问到了。
  147. while(front < rear){
  148. //取队头元素访问。
  149. int v = queue[front];
  150. front++;
  151. visit(v);
  152. //将该元素所指的第一个元素赋值
  153. Arc* currentArc = g.vlist[v].firstArc;
  154. //遍历邻接表中所有节点
  155. while(currentArc != NULL){
  156. //判断节点是否访问,如果未访问,添加到队列中
  157. if(visited[currentArc->adjvex] == 0){
  158. queue[rear] = currentArc->adjvex;
  159. rear++;
  160. //一定要在这里添加访问标志,否则会将访问过的节点反复添加。
  161. visited[currentArc->adjvex] = 1;
  162. }
  163. //继续寻找邻接表中的下一个可达节点
  164. currentArc = currentArc->next;
  165. }
  166. }
  167. }
  168. }
  169. int main()
  170. {
  171. Graph g;
  172. //手动设置图的数据
  173. // ManualInit(&g);
  174. //初始化预设图
  175. autoInit(&g);
  176. print(g);
  177. printf("DFS:\n");
  178. DFSTravers(g);
  179. //初始化访问数组
  180. initVisited();
  181. printf("\n");
  182. printf("BFS:\n");
  183. BFS2(g);
  184. return 0;
  185. }

代码中预设的图

如下是程序运行截图

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号