当前位置: 首页 > news >正文

C语言数据结构与算法——深度、广度优先搜索(DFS、BFS)

目录

一、深度优先搜索(Depth-First-Search 简称:DFS)

无向图的深度优先搜索

有向图的深度优先搜索

二、广度优先搜索(Breadth-First-Search 简称:BFS)

无向图的广度优先搜索

有向图的广度优先搜索

深度优先搜索(Depth-First Search,DFS)和广度优先搜索(Breadth-First Search,BFS)是两种常见的图遍历算法,它们在C语言中被广泛应用于解决各种数据结构和算法问题。这两种搜索算法都用于遍历图或树中的节点,以便查找特定的目标或执行其他相关任务。

一、深度优先搜索(Depth-First-Search 简称:DFS)

图的深度优先搜索(Depth First Search),和树的先序遍历比较类似。

它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

显然,深度优先搜索是一个递归的过程。

无向图的深度优先搜索

1.1 遍历过程:

  (1)从图中某个顶点v出发,访问v。

  (2)找出刚才第一个被顶点访问的邻接点。访问该顶点。以这个顶点为新的顶点,重复此步骤,直到访问过的顶点没有未被访问过的顶点为止。

  (3)返回到步骤(2)中的被顶点v访问的,且还没被访问的邻接点,找出该点的下一个未被访问的邻接点,访问该顶点。

  (4)重复(2) (3) 直到每个点都被访问过,遍历结束。

例无权图:

第1步:访问A。

第2步:访问(A的邻接点)B。  在第1步访问A之后,接下来应该访问的是A的邻接点,即"B、F、E"中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,B在"D、F和E"的前面,因此,先访问B。

第3步:访问(B的邻接点)C。在第2步访问B之后,接下来应该访问B的邻接点,即"F、D、C"中一个(A已经被访问过,就不算在内)。而由于C在D、F之前,先访问C。

第4步:访问(C的邻接点)D。在第3步访问C之后,接下来应该访问C的邻接点,即D。(B已经被访问过,就不算在内)。

第5步:访问(D的邻接点)E。

第6步:访问(E的邻接点)F。

故遍历结果为 A->B->C->D->E->F

有向图的深度优先搜索

第1步:访问A。

第2步:访问B。在访问A之后,接下来访问A的出边的另一个顶点,即顶点B。

第3步:访问C。 在访问了B之后,接下来应该访问的是B的出边的另一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,因此先访问C。

第4步:访问E。接下来访问C的出边的另一个顶点,即顶点E。

第5步:访问D。接下来访问E的出边的另一个顶点,即顶点B,D。顶点B已经被访问过,因此访问顶点D。

第6步:访问F。接下应该回溯"访问A的出边的另一个顶点F"。

第7步:访问G。

故遍历结果为A->b->c->E->D->F->G

二、广度优先搜索(Breadth-First-Search 简称:BFS)

广度优先搜索算法(Breadth First Search),又称为"宽度优先搜索"或"横向优先搜索",简称BFS。

它的思想是:从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。

换句话说,广度优先搜索遍历图的过程是以v为起点,由近至远,依次访问和v有路径相通且路径长度为1,2...的顶点。

无向图的广度优先搜索

第1步:访问A。
第2步:依次访问B,E,F。
    在访问了A之后,接下来访问A的邻接点。顶点ABCDEFG按照顺序存储,B在"E和F"的前面,因此,先访问B。再访问完B之后,再依次访问E,F。
第3步:依次访问C,D。
    在第2步访问完B,E,F之后,再依次访问它们的邻接点。首先访问B的邻接点C,再访问E的邻接点D。

因此访问顺序是:A->B->E->F->C->D

有向图的广度优先搜索

第1步:访问A。
第2步:访问B。
第3步:依次访问C,E,F。
    在访问了B之后,接下来访问B的出边的另一个顶点,即C,E,F。顶点ABCDEFG按照顺序存储,因此会先访问C,再依次访问E,F。
第4步:依次访问D,G。
    在访问完C,E,F之后,再依次访问它们的出边的另一个顶点。还是按照C,E,F的顺序访问,C的已经全部访问过了,那么就只剩下E,F;先访问E的邻接点D,再访问F的邻接点G。

因此访问顺序是:A->B->C->E->F->D->G

邻接矩阵图表示的"无向图"

/*** C: 邻接矩阵图表示的"无向图(Matrix Undirected Graph)"** @author skywang* @date 2014/04/18*/#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>#define MAX 100
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)  (sizeof(a)/sizeof(a[0]))// 邻接矩阵
typedef struct _graph
{char vexs[MAX];       // 顶点集合int vexnum;           // 顶点数int edgnum;           // 边数int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;/** 返回ch在matrix矩阵中的位置*/
static int get_position(Graph g, char ch)
{int i;for(i=0; i<g.vexnum; i++)if(g.vexs[i]==ch)return i;return -1;
}/** 读取一个输入字符*/
static char read_char()
{char ch;do {ch = getchar();} while(!isLetter(ch));return ch;
}/** 创建图(自己输入)*/
Graph* create_graph()
{char c1, c2;int v, e;int i, p1, p2;Graph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i] = read_char();}// 初始化"边"for (i = 0; i < pG->edgnum; i++){// 读取边的起始顶点和结束顶点printf("edge(%d):", i);c1 = read_char();c2 = read_char();p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);if (p1==-1 || p2==-1){printf("input error: invalid edge!\n");free(pG);return NULL;}pG->matrix[p1][p2] = 1;pG->matrix[p2][p1] = 1;}return pG;
}/** 创建图(用已提供的矩阵)*/
Graph* create_example_graph()
{char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'C'}, {'A', 'D'}, {'A', 'F'}, {'B', 'C'}, {'C', 'D'}, {'E', 'G'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);int i, p1, p2;Graph* pG;// 输入"顶点数"和"边数"if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++){pG->vexs[i] = vexs[i];}// 初始化"边"for (i = 0; i < pG->edgnum; i++){// 读取边的起始顶点和结束顶点p1 = get_position(*pG, edges[i][0]);p2 = get_position(*pG, edges[i][1]);pG->matrix[p1][p2] = 1;pG->matrix[p2][p1] = 1;}return pG;
}/** 返回顶点v的第一个邻接顶点的索引,失败则返回-1*/
static int first_vertex(Graph G, int v)
{int i;if (v<0 || v>(G.vexnum-1))return -1;for (i = 0; i < G.vexnum; i++)if (G.matrix[v][i] == 1)return i;return -1;
}/** 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1*/
static int next_vertix(Graph G, int v, int w)
{int i;if (v<0 || v>(G.vexnum-1) || w<0 || w>(G.vexnum-1))return -1;for (i = w + 1; i < G.vexnum; i++)if (G.matrix[v][i] == 1)return i;return -1;
}/** 深度优先搜索遍历图的递归实现*/
static void DFS(Graph G, int i, int *visited)
{                                   int w; visited[i] = 1;printf("%c ", G.vexs[i]);// 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走for (w = first_vertex(G, i); w >= 0; w = next_vertix(G, i, w)){if (!visited[w])DFS(G, w, visited);}}/** 深度优先搜索遍历图*/
void DFSTraverse(Graph G)
{int i;int visited[MAX];       // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){//printf("\n== LOOP(%d)\n", i);if (!visited[i])DFS(G, i, visited);}printf("\n");
}/** 广度优先搜索(类似于树的层次遍历)*/
void BFS(Graph G)
{int head = 0;int rear = 0;int queue[MAX];     // 辅组队列int visited[MAX];   // 顶点访问标记int i, j, k;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i]);queue[rear++] = i;  // 入队列}while (head != rear) {j = queue[head++];  // 出队列for (k = first_vertex(G, j); k >= 0; k = next_vertix(G, j, k)) //k是为访问的邻接顶点{if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k]);queue[rear++] = k;}}}}printf("\n");
}/** 打印矩阵队列图*/
void print_graph(Graph G)
{int i,j;printf("Martix Graph:\n");for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++)printf("%d ", G.matrix[i][j]);printf("\n");}
}void main()
{Graph* pG;// 自定义"图"(输入矩阵队列)//pG = create_graph();// 采用已有的"图"pG = create_example_graph();print_graph(*pG);       // 打印图DFSTraverse(*pG);       // 深度优先遍历BFS(*pG);               // 广度优先遍历
}

邻接表表示的"无向图"

/*** C: 邻接表表示的"无向图(List Undirected Graph)"** @author skywang* @date 2014/04/18*/#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>#define MAX 100
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)  (sizeof(a)/sizeof(a[0]))// 邻接表中表对应的链表的顶点
typedef struct _ENode
{int ivex;                   // 该边所指向的顶点的位置struct _ENode *next_edge;   // 指向下一条弧的指针
}ENode, *PENode;// 邻接表中表的顶点
typedef struct _VNode
{char data;              // 顶点信息ENode *first_edge;      // 指向第一条依附该顶点的弧
}VNode;// 邻接表
typedef struct _LGraph
{int vexnum;             // 图的顶点的数目int edgnum;             // 图的边的数目VNode vexs[MAX];
}LGraph;/** 返回ch在matrix矩阵中的位置*/
static int get_position(LGraph g, char ch)
{int i;for(i=0; i<g.vexnum; i++)if(g.vexs[i].data==ch)return i;return -1;
}/** 读取一个输入字符*/
static char read_char()
{char ch;do {ch = getchar();} while(!isLetter(ch));return ch;
}/** 将node链接到list的末尾*/
static void link_last(ENode *list, ENode *node)
{ENode *p = list;while(p->next_edge)p = p->next_edge;p->next_edge = node;
}/** 创建邻接表对应的图(自己输入)*/
LGraph* create_lgraph()
{char c1, c2;int v, e;int i, p1, p2;ENode *node1, *node2;LGraph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i].data = read_char();pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点printf("edge(%d): ", i);c1 = read_char();c2 = read_char();p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)malloc(sizeof(ENode));node2->ivex = p1;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;
}/** 创建邻接表对应的图(用已提供的数据)*/
LGraph* create_example_lgraph()
{char c1, c2;char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'C'}, {'A', 'D'}, {'A', 'F'}, {'B', 'C'}, {'C', 'D'}, {'E', 'G'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);int i, p1, p2;ENode *node1, *node2;LGraph* pG;if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){pG->vexs[i].data = vexs[i];pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点c1 = edges[i][0];c2 = edges[i][1];p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)malloc(sizeof(ENode));node2->ivex = p1;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;
}/** 深度优先搜索遍历图的递归实现*/
static void DFS(LGraph G, int i, int *visited)
{int w;ENode *node;visited[i] = 1;printf("%c ", G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){if (!visited[node->ivex])DFS(G, node->ivex, visited);node = node->next_edge;}
}/** 深度优先搜索遍历图*/
void DFSTraverse(LGraph G)
{int i;int visited[MAX];       // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i])DFS(G, i, visited);}printf("\n");
}/** 广度优先搜索(类似于树的层次遍历)*/
void BFS(LGraph G)
{int head = 0;int rear = 0;int queue[MAX];     // 辅组队列int visited[MAX];   // 顶点访问标记int i, j, k;ENode *node;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i].data);queue[rear++] = i;  // 入队列}while (head != rear) {j = queue[head++];  // 出队列node = G.vexs[j].first_edge;while (node != NULL){k = node->ivex;if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k].data);queue[rear++] = k;}node = node->next_edge;}}}printf("\n");
}/** 打印邻接表图*/
void print_lgraph(LGraph G)
{int i,j;ENode *node;printf("List Graph:\n");for (i = 0; i < G.vexnum; i++){printf("%d(%c): ", i, G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);node = node->next_edge;}printf("\n");}
}void main()
{LGraph* pG;// 自定义"图"(自己输入数据)//pG = create_lgraph();// 采用已有的"图"pG = create_example_lgraph();// 打印图print_lgraph(*pG);DFSTraverse(*pG);BFS(*pG);
}

邻接矩阵表示的"有向图"

/*** C: 邻接矩阵表示的"有向图(Matrix Directed Graph)"** @author skywang* @date 2014/04/18*/#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>#define MAX 100
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)  (sizeof(a)/sizeof(a[0]))// 邻接矩阵
typedef struct _graph
{char vexs[MAX];       // 顶点集合int vexnum;           // 顶点数int edgnum;           // 边数int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;/** 返回ch在matrix矩阵中的位置*/
static int get_position(Graph g, char ch)
{int i;for(i=0; i<g.vexnum; i++)if(g.vexs[i]==ch)return i;return -1;
}/** 读取一个输入字符*/
static char read_char()
{char ch;do {ch = getchar();} while(!isLetter(ch));return ch;
}/** 创建图(自己输入)*/
Graph* create_graph()
{char c1, c2;int v, e;int i, p1, p2;Graph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i] = read_char();}// 初始化"边"for (i = 0; i < pG->edgnum; i++){// 读取边的起始顶点和结束顶点printf("edge(%d):", i);c1 = read_char();c2 = read_char();p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);if (p1==-1 || p2==-1){printf("input error: invalid edge!\n");free(pG);return NULL;}pG->matrix[p1][p2] = 1;}return pG;
}/** 创建图(用已提供的矩阵)*/
Graph* create_example_graph()
{char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'B'}, {'B', 'C'}, {'B', 'E'}, {'B', 'F'}, {'C', 'E'}, {'D', 'C'}, {'E', 'B'}, {'E', 'D'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);int i, p1, p2;Graph* pG;// 输入"顶点数"和"边数"if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++){pG->vexs[i] = vexs[i];}// 初始化"边"for (i = 0; i < pG->edgnum; i++){// 读取边的起始顶点和结束顶点p1 = get_position(*pG, edges[i][0]);p2 = get_position(*pG, edges[i][1]);pG->matrix[p1][p2] = 1;}return pG;
}/** 返回顶点v的第一个邻接顶点的索引,失败则返回-1*/
static int first_vertex(Graph G, int v)
{int i;if (v<0 || v>(G.vexnum-1))return -1;for (i = 0; i < G.vexnum; i++)if (G.matrix[v][i] == 1)return i;return -1;
}/** 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1*/
static int next_vertix(Graph G, int v, int w)
{int i;if (v<0 || v>(G.vexnum-1) || w<0 || w>(G.vexnum-1))return -1;for (i = w + 1; i < G.vexnum; i++)if (G.matrix[v][i] == 1)return i;return -1;
}/** 深度优先搜索遍历图的递归实现*/
static void DFS(Graph G, int i, int *visited)
{                                   int w; visited[i] = 1;printf("%c ", G.vexs[i]);// 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走for (w = first_vertex(G, i); w >= 0; w = next_vertix(G, i, w)){if (!visited[w])DFS(G, w, visited);}}/** 深度优先搜索遍历图*/
void DFSTraverse(Graph G)
{int i;int visited[MAX];       // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){//printf("\n== LOOP(%d)\n", i);if (!visited[i])DFS(G, i, visited);}printf("\n");
}/** 广度优先搜索(类似于树的层次遍历)*/
void BFS(Graph G)
{int head = 0;int rear = 0;int queue[MAX];     // 辅组队列int visited[MAX];   // 顶点访问标记int i, j, k;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i]);queue[rear++] = i;  // 入队列}while (head != rear) {j = queue[head++];  // 出队列for (k = first_vertex(G, j); k >= 0; k = next_vertix(G, j, k)) //k是为访问的邻接顶点{if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k]);queue[rear++] = k;}}}}printf("\n");
}/** 打印矩阵队列图*/
void print_graph(Graph G)
{int i,j;printf("Martix Graph:\n");for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++)printf("%d ", G.matrix[i][j]);printf("\n");}
}void main()
{Graph* pG;// 自定义"图"(输入矩阵队列)//pG = create_graph();// 采用已有的"图"pG = create_example_graph();print_graph(*pG);       // 打印图DFSTraverse(*pG);       // 深度优先遍历BFS(*pG);               // 广度优先遍历
}

邻接表表示的"有向图"

/*** C: 邻接表表示的"有向图(List Directed Graph)"** @author skywang* @date 2014/04/18*/#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>#define MAX 100
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)  (sizeof(a)/sizeof(a[0]))// 邻接表中表对应的链表的顶点
typedef struct _ENode
{int ivex;                   // 该边所指向的顶点的位置struct _ENode *next_edge;   // 指向下一条弧的指针
}ENode, *PENode;// 邻接表中表的顶点
typedef struct _VNode
{char data;              // 顶点信息ENode *first_edge;      // 指向第一条依附该顶点的弧
}VNode;// 邻接表
typedef struct _LGraph
{int vexnum;             // 图的顶点的数目int edgnum;             // 图的边的数目VNode vexs[MAX];
}LGraph;/** 返回ch在matrix矩阵中的位置*/
static int get_position(LGraph g, char ch)
{int i;for(i=0; i<g.vexnum; i++)if(g.vexs[i].data==ch)return i;return -1;
}/** 读取一个输入字符*/
static char read_char()
{char ch;do {ch = getchar();} while(!isLetter(ch));return ch;
}/** 将node链接到list的末尾*/
static void link_last(ENode *list, ENode *node)
{ENode *p = list;while(p->next_edge)p = p->next_edge;p->next_edge = node;
}/** 创建邻接表对应的图(自己输入)*/
LGraph* create_lgraph()
{char c1, c2;int v, e;int i, p1, p2;ENode *node1, *node2;LGraph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i].data = read_char();pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点printf("edge(%d): ", i);c1 = read_char();c2 = read_char();p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex             = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);}return pG;
}/** 创建邻接表对应的图(用已提供的数据)*/
LGraph* create_example_lgraph()
{char c1, c2;char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'B'}, {'B', 'C'}, {'B', 'E'}, {'B', 'F'}, {'C', 'E'}, {'D', 'C'}, {'E', 'B'}, {'E', 'D'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);int i, p1, p2;ENode *node1, *node2;LGraph* pG;if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){pG->vexs[i].data = vexs[i];pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点c1 = edges[i][0];c2 = edges[i][1];p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);}return pG;
}/** 深度优先搜索遍历图的递归实现*/
static void DFS(LGraph G, int i, int *visited)
{int w;ENode *node;visited[i] = 1;printf("%c ", G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){if (!visited[node->ivex])DFS(G, node->ivex, visited);node = node->next_edge;}
}/** 深度优先搜索遍历图*/
void DFSTraverse(LGraph G)
{int i;int visited[MAX];       // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i])DFS(G, i, visited);}printf("\n");
}/** 广度优先搜索(类似于树的层次遍历)*/
void BFS(LGraph G)
{int head = 0;int rear = 0;int queue[MAX];     // 辅组队列int visited[MAX];   // 顶点访问标记int i, j, k;ENode *node;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i].data);queue[rear++] = i;  // 入队列}while (head != rear) {j = queue[head++];  // 出队列node = G.vexs[j].first_edge;while (node != NULL){k = node->ivex;if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k].data);queue[rear++] = k;}node = node->next_edge;}}}printf("\n");
}/** 打印邻接表图*/
void print_lgraph(LGraph G)
{int i,j;ENode *node;printf("List Graph:\n");for (i = 0; i < G.vexnum; i++){printf("%d(%c): ", i, G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);node = node->next_edge;}printf("\n");}
}void main()
{LGraph* pG;// 自定义"图"(自己输入数据)//pG = create_lgraph();// 采用已有的"图"pG = create_example_lgraph();// 打印图print_lgraph(*pG);DFSTraverse(*pG);BFS(*pG);
}

相关文章:

C语言数据结构与算法——深度、广度优先搜索(DFS、BFS)

目录 一、深度优先搜索&#xff08;Depth-First-Search 简称&#xff1a;DFS&#xff09; 无向图的深度优先搜索 有向图的深度优先搜索 二、广度优先搜索&#xff08;Breadth-First-Search 简称&#xff1a;BFS&#xff09; 无向图的广度优先搜索 有向图的广度优先搜索 深…...

Golang Channel 详细原理和使用技巧

1.简介 Channel(一般简写为 chan) 管道提供了一种机制:它在两个并发执行的协程之间进行同步&#xff0c;并通过传递与该管道元素类型相符的值来进行通信,它是Golang在语言层面提供的goroutine间的通信方式.通过Channel在不同的 goroutine中交换数据&#xff0c;在goroutine之间…...

CSS的浮动属性,web前端开发工程师

了解校招 知己知彼才能百战百胜&#xff0c;在准备校招之前&#xff0c;我们先要了解校招。 什么是校招&#xff1f; 校招&#xff0c;全称校园招聘&#xff0c;指企业招聘那些即将毕业的学生。校招主要分为三个部分&#xff1a;简历筛选&#xff0c;笔试&#xff0c;面试。 …...

Dubbo的集群容错方案

Dubbo提供了多种集群容错方案来保证分布式环境下的高可用性。这些容错方案可以在服务提供者不可用时&#xff0c;根据不同的业务需求和场景&#xff0c;选择不同的策略来处理。以下是Dubbo支持的一些主要集群容错方案&#xff1a; 1. Failover Cluster&#xff08;失败自动切换…...

两天学会微服务网关Gateway-Gateway路由规则

锋哥原创的微服务网关Gateway视频教程&#xff1a; Gateway微服务网关视频教程&#xff08;无废话版&#xff09;_哔哩哔哩_bilibiliGateway微服务网关视频教程&#xff08;无废话版&#xff09;共计17条视频&#xff0c;包括&#xff1a;1_Gateway简介、2_Gateway工作原理、3…...

three.js如何实现简易3D机房?(一)基础准备-下

three.js如何实现简易3D机房?(一)基础准备-上&#xff1a;http://t.csdnimg.cn/MCrFZ 目录 四、按需引入 五、导入模型 四、按需引入 index.vue文件中 <template><div class"three-area"><div class"three-box" ref"threeDemoRef…...

Android高级工程师面试实战,三幅图给你弄懂EventBus核心原理

阿里技术一面-35min 自我介绍 Android 有没有遇到OOM问题(有遇到内存泄漏问题)Handler机制ThreadLocalActivity启动到加载View过程View绘制过程LinearLayout (wrap_content) & TextView (match_parent) 最终结果???OKHttp(1. 为什么选择它&#xff1f; 2. 性能了解不…...

消息队列-kafka-服务端处理架构(架构,Topic文件结构,服务端数据的一致性)

服务端处理架构 资料来源于网络 网络线程池&#xff1a; 接受请求&#xff0c;num.network.threads&#xff0c;默认为 3&#xff0c;专门处理客户的发送的请求。 IO 线程池&#xff1a; num.io.threads&#xff0c;默认为 8&#xff0c;专门处理业务请求。也就是它不负责发…...

ES之API系列--index template(索引模板)的用法(有实例)

原文网址&#xff1a;ES之API系列--index template(索引模板)的用法(有实例)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍ElasticSearch的index template(索引模板)的用法(有实例)。 官网网址 https://www.elastic.co/guide/en/elasticsearch/reference/8.0/index-temp…...

electron+vue3全家桶+vite项目搭建【28】封装窗口工具类【2】窗口组,维护窗口关系

文章目录 引入实现效果思路主进程模块渲染进程模块测试效果 引入 demo项目地址 窗口工具类系列文章&#xff1a; 封装窗口工具类【1】雏形 封装窗口工具类【2】窗口组&#xff0c;维护窗口关系 封装窗口工具类【3】控制窗口定向移动 我们思考一下窗口间的关系&#xff0c;窗…...

docker安装ES和kibana

文章目录 一、安装Elasticsearch1. 安装Elasticsearch2. 安装IK分词器3. elasticsearch-head 监控的插件4. 配置跨域 二、安装kibana 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、安装Elasticsearch 1. 安装Elasticsearch 安装Elasticsearch参…...

uniapp微信小程序获取当前位置

uni-app微信小程序uni.getLocation获取位置&#xff1b;authorize scope.userLocation需要在app.json中声明permission&#xff1b;小程序用户拒绝授权后重新授权-CSDN博客...

HarmonyOS创建项目和应用—设置数据处理位置

项目和应用介绍 关于项目 项目是资源、应用的组织实体。资源包括服务器、数据库、存储&#xff0c;以及您的应用、终端用户的数据等。在您使用部分服务时&#xff0c;您是数据的控制者&#xff0c;数据将按照您设置的数据处理位置来存储在指定区域。 通常&#xff0c;您不需…...

3.1_2024ctf青少年比赛部分web题

php后门 根据x-powered-by知道php的版本 该版本存在漏洞&#xff1a; PHP 8.1.0-dev 开发版本后门 根据报错信息&#xff0c;进行提示&#xff0c;前 GET / HTTP/1.1 Host: challenge.qsnctf.com:31639 User-Agentt:12345678system(cat /flag);var_dump(2*3);zerodium12345678…...

Vue3:OptionsAPI 与 CompositionAPI的比较

1、Vue2 Vue2的API设计是Options&#xff08;配置&#xff09;风格的。 Options API 的弊端 Options类型的 API&#xff0c;数据、方法、计算属性等&#xff0c;是分散在&#xff1a;data、methods、computed中的&#xff0c;若想新增或者修改一个需求&#xff0c;就需要分别…...

Rust! 无VDom! 尤雨溪解析 Vue.js 2024 新特性

视频号搜索“云前端”观看视频版 在 VueJS Amsterdam 2024 大会首日&#xff0c;Vue 创始人 Evan You 进行了开场主旨演讲。他首先回顾了 Vue 十年以来的累累硕果&#xff0c;指出 VueJS 从一个视图层工具&#xff0c;成功演化出全流程的社区生态。 Vue 3.4 谈到 Vue 3 的发展时…...

Windows上websocket客户端连接定时存储消息到文件并加载文件定时发送服务端工具实现

场景 在业务开发中&#xff0c;需要对接三方websocket协议数据或者连接并存储线上websocket协议数据&#xff0c;需要使用websocket客户端 连接线上的websocket服务端获取并存储数据&#xff0c;然后将数据存储成文件格式可移植&#xff0c;并将数据复制 到本地&#xff0c;…...

【STM32+OPENMV】二维云台颜色识别及追踪

一、准备工作 有关OPENMV最大色块追踪及与STM32通信内容&#xff0c;详情见【STM32HAL】与OpenMV通信 有关七针OLED屏显示内容&#xff0c;详情见【STM32HAL】七针OLED(SSD1306)配置(SPI版) 二、所用工具 1、芯片&#xff1a;STM32F407ZGT6 2、CUBEMX配置软件 3、KEIL5 4…...

JavaScript基础3之面向对象关于面向过程、函数式编程、对比、构造函数、原型

JavaScript基础 面向对象面向过程函数式编程命令式编程函数式编程特性副作用透明引用不可变变量函数是一等公民 常见的函数式编程模型 面向对象为什么要使用面向对象封装继承多态 对比面向过程函数式编程面向对象 构造函数原型constructor使用场景 对象原型 面向对象 面向过程…...

运用Tensorflow进行目标检测

对象检测是一种计算机视觉技术&#xff0c;它使软件系统能够从给定的图像或视频中检测、定位并跟踪物体。对象检测的一个特殊属性是它能识别对象的类别&#xff08;如人、桌子、椅子等&#xff09;并在给定图像中指出其具体位置坐标。这个位置通常通过在物体周围绘制一个边界框…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...

HTTPS证书一年多少钱?

HTTPS证书作为保障网站数据传输安全的重要工具&#xff0c;成为众多网站运营者的必备选择。然而&#xff0c;面对市场上种类繁多的HTTPS证书&#xff0c;其一年费用究竟是多少&#xff0c;又受哪些因素影响呢&#xff1f; 首先&#xff0c;HTTPS证书通常在PinTrust这样的专业平…...