当前位置:   article > 正文

C语言基于邻接表的图的深度优先、广度优先遍历_邻接表的深度优先遍历和广度优先遍历

邻接表的深度优先遍历和广度优先遍历

目录

1 深度优先(Depth_First Search)

2 广度优先(Broadth_First Search)

3 基于邻接表的深度优先、广度优先遍历

4 源代码示例

4.1 深度优先

4.2广度优先


假设有无向图G = (V,E),标志数组visited [ n ]

(1)点集 V = { v1,v2,v3,v4,v5,v6,v7,v8 }

     边集 E = {  (v1,v2),(v1,v3),(v2,v4),(v2,v5),(v3,v6),(v3,v7),(v4,v8),(v5,v8),(v6,v7) }

(2)visited [ n ] (n为图中顶点个数,初始元素都为0)

若相应节点被访问过,则visited [ i ] 为 1;否则visited [ i ] 为 0

1 深度优先(Depth_First Search)

选定一个节点并遍历后,遍历该节点的第一个未被遍历邻接点;从刚遍历的节点开始,遍历该节点第一个未被遍历邻接点;

如此重复(深度含义由此可知)。若顶点的所有邻接点都被遍历,则检测visited[]数组,从元素值为0的节点开始遍历。

以上图为例:

从顶点V1开始遍历,然后遍历V1的第一个未被遍历邻接点V2;

从V2开始,遍历V2的第一个未被遍历邻接点V4;

从V4开始,遍历V4的第一个未被遍历邻接点V8;

从V8开始,遍历V8的第一个未被遍历邻接点V5;

从V5开始,发现V5所有邻接点都被遍历,则检测visited[]数组,发现数组中第一个未被遍历邻接点V3;

从V3开始,遍历V3的第一个未被遍历邻接点V6;

从V6开始,遍历V6的第一个未被遍历邻接点V7;

结束。

综上,深度优先序列:1,2,4,8,5,3,6,7。

2 广度优先(Broadth_First Search)

假设从顶点V1开始遍历,则遍历完V1的所有未被遍历邻接点,然后从所有邻接点中挑选出(按顺序从小到大)下标第一小的节点,再遍历该节点所有未被遍历邻接点;第二小……;第三小……。如此循环(广度含义由此可知)。若顶点的所有邻接点都被遍历,则检测visited[]数组,从元素值为0(未遍历)的节点开始遍历。

以上图为例:

从顶点V1开始遍历,然后遍历V1的所有未被遍历邻接点V2,V3;

从V2开始,遍历V2所有未被遍历邻接点V4,V5;

从V3开始,遍历V3所有未被遍历邻接点V6,V7;

从V4开始,遍历V4所有未被遍历邻接点V8。结束

综上,广度优先序列:1,2,3,4,5,6,7,8。

3 基于邻接表的深度优先、广度优先遍历

关于图的邻接表存储,可参考我的博客C语言图的邻接表存储

以上图为例,其邻接表结构如下所示:

  1. 顶点 1 :[ 2 ] -> [ 3 ] -> NULL
  2. 顶点 2 :[ 1 ] -> [ 4 ] -> [ 5 ] -> NULL
  3. 顶点 3 :[ 1 ] -> [ 6 ] -> [ 7 ] -> NULL
  4. 顶点 4 :[ 2 ] -> [ 8 ] -> NULL
  5. 顶点 5 :[ 2 ] -> [ 8 ] -> NULL
  6. 顶点 6 :[ 3 ] -> [ 7 ] -> NULL
  7. 顶点 7 :[ 3 ] -> [ 6 ] -> NULL
  8. 顶点 8 :[ 4 ] -> [ 5 ] -> NULL

深度遍历

对所有节点(1、2……8),假设从1开始访问,每访问一个节点,便将其标志数组(visited)对应位置置1,然后访问其第一个未被访问的邻接点,接着对当前访问的顶点执行同样操作若所有邻接点都被访问,则遍历标志数组,找到第一个未被访问的节点开始访问。

如上表:1开始;然后2;2第一个未被访问的邻接点4;4第一个未被访问的邻接点8;8第一个未被访问的邻接点5;5的所有邻接点都被访问、则遍历标志数组、找到第一个未被访问的3;3第一个未被访问的邻接点6;6第一个未被访问的邻接点7;结束。

广度遍历 

对所有节点(1、2……8),假设从1开始访问,每访问一个节点,便将其标志数组(visited)对应位置置1,然后访问其所有未被访问的邻接点,接着对节点所有邻接点按顺序重复同样操作。若节点邻接点都被访问,则遍历标志数组,找到第一个未被访问的节点开始访问。

如上表:1开始;访问2、3;然后从1的第一个邻接点2开始、访问2的邻接点4、5;3开始、访问6、7;4开始、访问8;5开始、其所有邻接点都被访问、跳过;……

4 源代码示例

4.1 深度优先

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_VERTEX_NUM 100 // 图中最大节点数
  4. typedef char VertexType;
  5. // 边表节点
  6. typedef struct node {
  7. VertexType adjvex; // 与顶点相连的邻接点下标(adjoin:邻接)
  8. struct node* next; // 指向顶点的下一个邻接点
  9. } EdgeNode;
  10. // 顶点结构
  11. typedef struct vnode {
  12. VertexType vex; // 存储顶点名
  13. EdgeNode* firstedge; // 边表头指针,指向顶点第一个邻接点
  14. } VertexNode, AdjList[MAX_VERTEX_NUM];
  15. typedef struct {
  16. AdjList adjlist; // 描述图结构的邻接表
  17. int vexnum; // 节点的数目
  18. int edgenum; // 边的数目
  19. } ALGraph; // adjacency list:邻接表
  20. void CreateALG(ALGraph* ALG); // 邻接表法创建图
  21. void TraverseALG(ALGraph ALG); // 输出图ALG的邻接表
  22. void DFSTraverseALG(ALGraph ALG); // 深度优先遍历以邻接表存储的图ALG
  23. void DFSALG(ALGraph ALG, int i); // 以Vi为出发点对邻接表存储的图ALG开始DFS搜索
  24. // 定位节点vertex,并将其下标赋给index
  25. void LocateVex(ALGraph ALG, VertexType vertex, int* index);
  26. int visited[MAX_VERTEX_NUM]; // 标志数组
  27. int main(void)
  28. {
  29. ALGraph g;
  30. CreateALG(&g);
  31. printf("------------------------------\n");
  32. printf("vexnum = %d ; edgenum = %d\n", g.vexnum, g.edgenum);
  33. printf("------------------------------\n");
  34. TraverseALG(g);
  35. printf("------------------------------\n");
  36. DFSTraverseALG(g);
  37. return 0;
  38. }
  39. void CreateALG(ALGraph* ALG)
  40. {
  41. VertexType ch;
  42. int i = 0, count = 0;
  43. EdgeNode* temp;
  44. printf("请输入图的顶点:");
  45. // 建立顶点表
  46. while ((ch = getchar()) != '\n') {
  47. ALG->adjlist[i].vex = ch;
  48. ALG->adjlist[i].firstedge = NULL;
  49. i++;
  50. }
  51. ALG->vexnum = i; // 顶点数
  52. // 头插法建立顶点的邻接边表
  53. for (i = 0; i < ALG->vexnum; i++) {
  54. printf("请输入顶点 %c 的邻接顶点:", ALG->adjlist[i].vex);
  55. // 按下回车结束邻接点的创建
  56. while ((ch = getchar()) != '\n') {
  57. temp = (EdgeNode*)malloc(sizeof(EdgeNode));
  58. temp->adjvex = ch;
  59. temp->next = ALG->adjlist[i].firstedge;
  60. ALG->adjlist[i].firstedge = temp;
  61. count++;
  62. }
  63. }
  64. // 无向图中每条边连接两个顶点,故:节点总度数 = 边数 * 2
  65. ALG->edgenum = count / 2;
  66. }
  67. void TraverseALG(ALGraph ALG)
  68. {
  69. int i;
  70. EdgeNode* temp;
  71. if (ALG.vexnum == 0) {
  72. printf("图为空\n");
  73. return;
  74. }
  75. // 遍历图
  76. for (i = 0; i < ALG.vexnum; i++) {
  77. printf("顶点 %c :", ALG.adjlist[i].vex);
  78. temp = ALG.adjlist[i].firstedge;
  79. // 输出图的信息
  80. while (temp) {
  81. printf("[ %c ] -> ", temp->adjvex);
  82. temp = temp->next;
  83. }
  84. printf("NULL\n");
  85. }
  86. }
  87. // 深度优先遍历以邻接表存储的图ALG
  88. void DFSTraverseALG(ALGraph ALG)
  89. {
  90. int i;
  91. // 初始化标志数组
  92. for (i = 0; i < ALG.vexnum; i++) {
  93. visited[i] = 0;
  94. }
  95. printf("图的深度优先遍历序列:");
  96. // 从第一个节点开始DFS搜索
  97. for (i = 0; i < ALG.vexnum; i++) {
  98. if (!visited[i]) {
  99. DFSALG(ALG, i);
  100. }
  101. }
  102. }
  103. // 以下标为i的节点为出发点对图ALG开始DFS搜索
  104. void DFSALG(ALGraph ALG, int i)
  105. {
  106. EdgeNode* temp;
  107. int index;
  108. printf("%c, ", ALG.adjlist[i].vex);
  109. visited[i] = 1; // 标记节点i已被访问
  110. temp = ALG.adjlist[i].firstedge;
  111. while (temp) {
  112. LocateVex(ALG, temp->adjvex, &index);
  113. // 若以index为下标的节点未被遍历,则遍历。并从该节点开始进行下一轮DFS搜索
  114. if (!visited[index]) {
  115. DFSALG(ALG, index);
  116. }
  117. // 若以index为下标的节点被遍历,则寻找节点的下一个邻接点
  118. temp = temp->next;
  119. }
  120. }
  121. void LocateVex(ALGraph ALG, VertexType vertex, int* index)
  122. {
  123. int i;
  124. for (i = 0; i < ALG.vexnum; i++) {
  125. if (ALG.adjlist[i].vex == vertex) {
  126. *index = i; // 将节点vertex的下标赋给index
  127. return;
  128. }
  129. }
  130. }
  1. C:\WINDOWS\system32\cmd.exe /c (gcc -fexec-charset=gbk 1.c -o 1 ^&^& 1 ^&^& del 1.exe)
  2. 请输入图的顶点:12345678
  3. 请输入顶点 1 的邻接顶点:32
  4. 请输入顶点 2 的邻接顶点:541
  5. 请输入顶点 3 的邻接顶点:761
  6. 请输入顶点 4 的邻接顶点:82
  7. 请输入顶点 5 的邻接顶点:82
  8. 请输入顶点 6 的邻接顶点:73
  9. 请输入顶点 7 的邻接顶点:63
  10. 请输入顶点 8 的邻接顶点:54
  11. ------------------------------
  12. vexnum = 8 ; edgenum = 9
  13. ------------------------------
  14. 顶点 1 :[ 2 ] -> [ 3 ] -> NULL
  15. 顶点 2 :[ 1 ] -> [ 4 ] -> [ 5 ] -> NULL
  16. 顶点 3 :[ 1 ] -> [ 6 ] -> [ 7 ] -> NULL
  17. 顶点 4 :[ 2 ] -> [ 8 ] -> NULL
  18. 顶点 5 :[ 2 ] -> [ 8 ] -> NULL
  19. 顶点 6 :[ 3 ] -> [ 7 ] -> NULL
  20. 顶点 7 :[ 3 ] -> [ 6 ] -> NULL
  21. 顶点 8 :[ 4 ] -> [ 5 ] -> NULL
  22. ------------------------------
  23. 图的深度优先遍历序列:1, 2, 4, 8, 5, 3, 6, 7, Hit any key to close this window...

4.2广度优先

广度优先遍历时,需要用队列辅助操作,关于队列的实现,可参考我的博客C语言实现顺序队列、循环队列、链式队列

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_VERTEX_NUM 100 // 图中最大节点数
  4. typedef char VertexType; // 定义节点名为char型
  5. // 边表节点
  6. typedef struct node {
  7. VertexType adjvex; // 与顶点相连的邻接点下标(adjoin:邻接)
  8. struct node* next; // 指向顶点的下一个邻接点
  9. } EdgeNode;
  10. // 顶点结构
  11. typedef struct vnode {
  12. VertexType vex; // 存储顶点名
  13. EdgeNode* firstedge; // 边表头指针,指向顶点第一个邻接点
  14. } VertexNode, AdjList[MAX_VERTEX_NUM];
  15. // 描述图结构的邻接表
  16. typedef struct {
  17. AdjList adjlist;
  18. int vexnum; // 节点的数目
  19. int edgenum; // 边的数目
  20. } ALGraph; // adjacency list:邻接表
  21. int visited[MAX_VERTEX_NUM]; // 标志数组
  22. void CreateALG(ALGraph* ALG); // 邻接表法创建图
  23. void TraverseALG(ALGraph ALG); // 输出图ALG的邻接表
  24. void BFSTraverseALG(ALGraph ALG); // 广度优先遍历以邻接表存储的图ALG
  25. // 定位节点vertex,并将其下标赋给index
  26. void LocateVex(ALGraph ALG, VertexType vertex, int* index);
  27. /*----------------定义一个循环队列-------------------*/
  28. #define CQ_INIT_SIZE 100 // 队列初始容量
  29. typedef int dataType;
  30. typedef struct {
  31. dataType* data; //存储队列元素
  32. int front; //指向队列中第一个元素
  33. int rear; //指向队列中最后一个元素下一位置
  34. int cqCapacity; //最多能容纳的元素个数(队列容量)
  35. } CQueue;
  36. CQueue* initCQueue(); //创建一个空循环队列
  37. int push(CQueue* Q, dataType x); //将元素x入队。操作成功返回1,失败返回0
  38. int pop(CQueue* Q, dataType* x); //队首元素出队,并将其值赋给x。操作成功返回1,失败返回0
  39. int isEmpty(CQueue* Q); //队列空返回1,否则返回0
  40. int isFull(CQueue* Q); //队列满返回1,否则返回0
  41. int main(void)
  42. {
  43. ALGraph g;
  44. CreateALG(&g);
  45. printf("------------------------------\n");
  46. printf("vexnum = %d ; edgenum = %d\n", g.vexnum, g.edgenum);
  47. printf("------------------------------\n");
  48. TraverseALG(g);
  49. printf("------------------------------\n");
  50. BFSTraverseALG(g);
  51. return 0;
  52. }
  53. void CreateALG(ALGraph* ALG)
  54. {
  55. VertexType ch;
  56. int i = 0, count = 0;
  57. EdgeNode* temp;
  58. printf("请输入图的顶点:");
  59. // 建立顶点表
  60. while ((ch = getchar()) != '\n') {
  61. ALG->adjlist[i].vex = ch;
  62. ALG->adjlist[i].firstedge = NULL;
  63. i++;
  64. }
  65. ALG->vexnum = i; // 顶点数
  66. // 头插法建立顶点的邻接边表
  67. for (i = 0; i < ALG->vexnum; i++) {
  68. printf("请输入顶点 %c 的邻接顶点:", ALG->adjlist[i].vex);
  69. // 按下回车结束邻接点的创建
  70. while ((ch = getchar()) != '\n') {
  71. temp = (EdgeNode*)malloc(sizeof(EdgeNode));
  72. temp->adjvex = ch;
  73. temp->next = ALG->adjlist[i].firstedge;
  74. ALG->adjlist[i].firstedge = temp;
  75. count++;
  76. }
  77. }
  78. ALG->edgenum = count / 2;
  79. // 无向图中每条边连接两个顶点,故:节点总度数 = 边数 * 2
  80. }
  81. void TraverseALG(ALGraph ALG)
  82. {
  83. int i;
  84. EdgeNode* index;
  85. // 若图为空,则停止遍历
  86. if (ALG.vexnum == 0) {
  87. printf("图为空\n");
  88. return;
  89. }
  90. // 遍历图
  91. for (i = 0; i < ALG.vexnum; i++) {
  92. printf("顶点 %c :", ALG.adjlist[i].vex);
  93. index = ALG.adjlist[i].firstedge;
  94. // 以邻接表形式输出图的信息
  95. while (index) {
  96. printf("[ %c ] -> ", index->adjvex);
  97. index = index->next;
  98. }
  99. printf("NULL\n");
  100. }
  101. }
  102. // 广度优先遍历以邻接表存储的图ALG
  103. void BFSTraverseALG(ALGraph ALG)
  104. {
  105. int i, index; // index为当前访问节点的索引
  106. char ch; // 从节点ch开始对图进行BFS搜索
  107. EdgeNode* temp;
  108. CQueue* que = initCQueue();
  109. // 初始化标志数组
  110. for (i = 0; i < ALG.vexnum; i++) {
  111. visited[i] = 0;
  112. }
  113. printf("请输入开始节点:");
  114. scanf("%c", &ch);
  115. LocateVex(ALG, ch, &index); // 将开始节点ch的下标赋给index
  116. printf("图的广度优先遍历序列:");
  117. if (!visited[index]) {
  118. push(que, index); // 开始节点入队,并修改visited数组
  119. visited[index] = 1;
  120. // 当队列不空时
  121. while (!isEmpty(que)) {
  122. pop(que, &index); // 队首元素出队并访问
  123. printf("%c, ", ALG.adjlist[index].vex);
  124. temp = ALG.adjlist[index].firstedge;
  125. // 将节点的所有邻接点入队
  126. while (temp) {
  127. LocateVex(ALG, temp->adjvex, &index);
  128. // 若节点未被遍历,则入队并修改visited数组
  129. if (!visited[index]) {
  130. push(que, index);
  131. visited[index] = 1;
  132. }
  133. temp = temp->next;
  134. }
  135. }
  136. }
  137. }
  138. void LocateVex(ALGraph ALG, VertexType vertex, int* index)
  139. {
  140. int i;
  141. for (i = 0; i < ALG.vexnum; i++) {
  142. if (ALG.adjlist[i].vex == vertex) {
  143. *index = i; // 将节点vertex的下标赋给index
  144. return;
  145. }
  146. }
  147. printf("节点 %c 定位失败!\n", vertex);
  148. }
  149. // 建立空队列
  150. CQueue* initCQueue()
  151. {
  152. CQueue* Q = (CQueue*)malloc(sizeof(CQueue));
  153. Q->data = (dataType*)malloc(CQ_INIT_SIZE * sizeof(dataType));
  154. Q->front = 0;
  155. Q->rear = 0;
  156. Q->cqCapacity = CQ_INIT_SIZE;
  157. return Q;
  158. }
  159. int isFull(CQueue* Q)
  160. {
  161. return (Q->rear + 1) % Q->cqCapacity == Q->front ? 1 : 0;
  162. }
  163. int isEmpty(CQueue* Q)
  164. {
  165. return Q->front == Q->rear ? 1 : 0;
  166. }
  167. // 入队
  168. int push(CQueue* Q, dataType x)
  169. {
  170. if (isFull(Q)) {
  171. // 若达到最大容量,则将新容量扩大至旧容量 1.5 倍
  172. int increment = Q->cqCapacity / 2;
  173. Q->data = (dataType*)realloc(Q->data,
  174. (Q->cqCapacity + increment) * sizeof(dataType));
  175. if (!Q->data) {
  176. return 0;
  177. }
  178. Q->cqCapacity += increment;
  179. }
  180. Q->data[Q->rear] = x;
  181. Q->rear = (Q->rear + 1) % Q->cqCapacity;
  182. return 1;
  183. }
  184. // 出队
  185. int pop(CQueue* Q, dataType* x)
  186. {
  187. if (isEmpty(Q)) {
  188. return 0;
  189. } else {
  190. *x = Q->data[Q->front];
  191. Q->front = (Q->front + 1) % Q->cqCapacity;
  192. return 1;
  193. }
  194. }

  1. C:\WINDOWS\system32\cmd.exe /c (gcc -fexec-charset=gbk 1.c -o 1 ^&^& 1 ^&^& del 1.exe)
  2. 请输入图的顶点:12345678
  3. 请输入顶点 1 的邻接顶点:32
  4. 请输入顶点 2 的邻接顶点:541
  5. 请输入顶点 3 的邻接顶点:761
  6. 请输入顶点 4 的邻接顶点:82
  7. 请输入顶点 5 的邻接顶点:82
  8. 请输入顶点 6 的邻接顶点:73
  9. 请输入顶点 7 的邻接顶点:63
  10. 请输入顶点 8 的邻接顶点:54
  11. ------------------------------
  12. vexnum = 8 ; edgenum = 9
  13. ------------------------------
  14. 顶点 1 :[ 2 ] -> [ 3 ] -> NULL
  15. 顶点 2 :[ 1 ] -> [ 4 ] -> [ 5 ] -> NULL
  16. 顶点 3 :[ 1 ] -> [ 6 ] -> [ 7 ] -> NULL
  17. 顶点 4 :[ 2 ] -> [ 8 ] -> NULL
  18. 顶点 5 :[ 2 ] -> [ 8 ] -> NULL
  19. 顶点 6 :[ 3 ] -> [ 7 ] -> NULL
  20. 顶点 7 :[ 3 ] -> [ 6 ] -> NULL
  21. 顶点 8 :[ 4 ] -> [ 5 ] -> NULL
  22. ------------------------------
  23. 请输入开始节点:1
  24. 图的广度优先遍历序列:1, 2, 3, 4, 5, 6, 7, 8, Hit any key to close this window...

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/509663
推荐阅读
相关标签
  

闽ICP备14008679号