【427】Graph 实现 以及 DFS & BFS
目录:
1. Graph 实现
1.1 二维数组实现
GraphAM.c
// GraphAM.c: an adjacency matrix implementation #include <stdio.h> #include <stdlib.h> #include "Graph.h" struct graphRep { int nV; // #vertices int nE; // #edges int **edges; // matrix of Booleans ... THIS IS THE ADJACENCY MATRIX }; Graph newGraph(int numVertices) { Graph g = NULL; if (numVertices < 0) { fprintf(stderr, "newgraph: invalid number of vertices\n"); } else { g = malloc(sizeof(struct graphRep)); if (g == NULL) { fprintf(stderr, "newGraph: out of memory\n"); exit(1); } g->edges = malloc(numVertices * sizeof(int *)); if (g->edges == NULL) { fprintf(stderr, "newGraph: out of memory\n"); exit(1); } int v; for (v = 0; v < numVertices; v++) { g->edges[v] = malloc(numVertices * sizeof(int)); if (g->edges[v] == NULL) { fprintf(stderr, "newGraph: out of memory\n"); exit(1); } for (int j = 0; j < numVertices; j++) { g->edges[v][j] = 0; } } g->nV = numVertices; g->nE = 0; } return g; } Graph freeGraph(Graph g) { if (g != NULL) { int i; for (i = 0; i < g->nV; i++) { free(g->edges[i]); // free the mallocs for each row ... } free(g->edges); // now the malloc for the edges array ... free(g); // now the malloc for the graph rep } return g; } void showGraph(Graph g) { // print a graph if (g == NULL) { printf("NULL graph\n"); } else { printf("V=%d, E=%d\n", g->nV, g->nE); int i; for (i = 0; i < g->nV; i++) { int nshown = 0; int j; for (j = 0; j < g->nV; j++) { if (g->edges[i][j] != 0) { printf("%d-%d ", i, j); nshown++; } } if (nshown > 0) { printf("\n"); } } } return; } static int validV(Graph g, Vertex v) { // checks if v is in graph return (v >= 0 && v < g->nV); } Edge newE(Vertex v, Vertex w) { // create an edge from v to w Edge e = {v, w}; return e; } void showE(Edge e) { // print an edge printf("%d-%d", e.v, e.w); return; } int isEdge(Graph g, Edge e) { // return 1 if edge found, otherwise 0 int found = 0; if (g != NULL && validV(g, e.v) && validV(g, e.w)) { found = (g->edges[e.v][e.w] == 1); } return found; } void insertE(Graph g, Edge e) { // insert an edge into a graph if (g == NULL) { fprintf(stderr, "insertE: graph not initialised\n"); } else { if (!validV(g, e.v) || !validV(g, e.w)) { fprintf(stderr, "insertE: invalid vertices %d-%d\n", e.v, e.w); } else { if (isEdge(g, e) == 0) { // increment nE only if it is new g->nE++; } g->edges[e.v][e.w] = 1; g->edges[e.w][e.v] = 1; } } return; } void removeE(Graph g, Edge e) { // remove an edge from a graph if (g == NULL) { fprintf(stderr, "removeE: graph not initialised\n"); } else { if (!validV(g, e.v) || !validV(g, e.w)) { fprintf(stderr, "removeE: invalid vertices\n"); } else { if (isEdge(g, e) == 1) { // is edge there? g->edges[e.v][e.w] = 0; g->edges[e.w][e.v] = 0; g->nE--; } } } return; }
1.2 Linked List 实现
GraphAL.c
// GraphAL.c: an adjacency list implementation #include <stdio.h> #include <stdlib.h> #include "Graph.h" typedef struct node *list; struct node { Vertex name; list next; }; struct graphRep { int nV; // #vertices int nE; // #edges list *edges; // array of linked lists ... THIS IS THE ADJACENCY LIST }; Graph newGraph(int numVertices) { Graph g = NULL; if (numVertices < 0) { fprintf(stderr, "newgraph: invalid number of vertices\n"); } else { g = malloc(sizeof(struct graphRep)); if (g == NULL) { fprintf(stderr, "newGraph: out of memory\n"); exit(1); } g->edges = malloc(numVertices * sizeof(int *)); if (g->edges == NULL) { fprintf(stderr, "newGraph: out of memory\n"); exit(1); } int v; for (v = 0; v < numVertices; v++) { g->edges[v] = NULL; } g->nV = numVertices; g->nE = 0; } return g; } Graph freeGraph(Graph g) { if (g != NULL) { int i; for (i = 0; i < g->nV; i++) { List node = g->edges[i]; // maybe NULL, maybe points to first node while (node != NULL) { List tmp = node; // save the node node = node->next; // move onto the next node free(tmp); // free the saved node } } free(g->edges); // now the malloc for the edges array ... free(g); // now the malloc for the graph rep } return g; } void showGraph(Graph g) { // print a graph if (g == NULL) { printf("NULL graph\n"); } else { printf("V=%d, E=%d\n", g->nV, g->nE); int i; for (i = 0; i < g->nV; i++) { int nshown = 0; list listV = g->edges[i]; while (listV != NULL) { //printf("g->edges[%d]=%p\n",i , listV); printf("%d-%d ", i, listV->name); nshown++; listV = listV->next; } if (nshown > 0) { printf("\n"); } } } return; } static int validV(Graph g, Vertex v) { // checks if v is in graph return (v >= 0 && v < g->nV); } Edge newE(Vertex v, Vertex w) { Edge e = {v, w}; return e; } void showE(Edge e) { // print an edge printf("%d-%d", e.v, e.w); return; } int isEdge(Graph g, Edge e) { // return 1 if edge found, otherwise 0 // a linear search for edge 'e': return 1 if edge found, 0 otherwise int found = 0; if (g != NULL && validV(g, e.v) && validV(g, e.w)) { list curr; for (curr = g->edges[e.v]; curr != NULL && !found; curr = curr->next) { if (curr->name == e.w) { found = 1; } } } return found; } void insertE(Graph g, Edge e){ if (g == NULL) { fprintf(stderr, "insertE: graph not initialised\n"); } else { if (!validV(g, e.v) || !validV(g, e.w)) { fprintf(stderr, "insertE: invalid vertices %d-%d\n", e.v, e.w); } else { if (isEdge(g, e) == 0) { list newnodev = malloc(sizeof(struct node)); list newnodew = malloc(sizeof(struct node)); if (newnodev == NULL || newnodew == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } newnodev->name = e.w; // put in the data newnodev->next = g->edges[e.v]; // link to the existing list attached to e.v g->edges[e.v] = newnodev; // link e.v to new node newnodew->name = e.v; newnodew->next = g->edges[e.w]; g->edges[e.w] = newnodew; g->nE++; } } } return; } void removeE(Graph g, Edge e) { int success = 0; List n = g->edges[v]; // n is the start node List p = NULL; // p is previous node to n while (n != NULL && !success){ // linear search for w if (n->name == w) { List nn = n->next; // we've found w, we want to skip over it if (p == NULL) { // if w is first node, p will be NULL g->edges[v] = nn; } else { p->next = nn; } free(n); success = 1; } p = n; n = n->next; } return success; }
2. DFS:Deep First Search,深度优先搜索
2.1 通过 stack 来实现(后进先出)
- 首先 push 进去一个 vertex(任意一个顶点,一般选择 0)
- 以下为 while 循环内容,条件为栈不为空
- 然后 pop 出来,并记录 v 值
- 判断 v 是否访问过
- 从大到小遍历所有与 v 相连的 w
- 并将所有的 w 依次 push 入栈
- 执行完之后,进行下一轮的循环
- 首先 pop 出来的是上一轮最后入栈的 w
- 然后继续遍历次 w 的所有邻接点
- 不停的便利下去,直到遍历到头了
- 然后再不停的 pop,遇到访问的顶点直接就 pass 了
- 遇到没有访问的顶点就继续遍历
- 往复循环
- 直到最后所有的点都 pop 了,stack 为空的时候停止
- 此代码每次会将所有的邻接点压入栈,然后在出栈的时候会判断是否访问过,没有访问过就压入所有邻接点,否则跳过了。。。不过都会打印当前的 quack
dfsquack.c
// dfsquack.c: traverse a graph using DFS and a stack implementation #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" void dfs(Graph, Vertex, int); #define WHITESPACE 100 int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void dfs(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *v = malloc(numV * sizeof(int)); if (v == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } int i; for (i=0; i<numV; i++) { v[i] = UNVISITED; } return v; } void showArray(int *v, int numV) { int i; printf("Visited: {"); for (i=0; i<numV; i++) { printf("%d", v[i]); if (i <= numV-2) { printf(", "); } } printf("}\n"); return; } int *visited = mallocArray(numV); Quack s = createQuack(); push(v, s); showQuack(s); int order = 0; while (!isEmptyQuack(s)) { v = pop(s); if (visited[v] == UNVISITED) { showArray(visited, numV); //printf("visited[%d]=%d\n", v, order); visited[v] = order++; Vertex w; for (w=numV-1; w>=0; w--) { if (isEdge(g, newE(v,w))) { // if (isEdge(g, newE(v,w)) && visited[w] == UNVISITED) push (w, s); } } } showQuack(s); } showArray(visited, numV); free(visited); return; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { showGraph(g); dfs(g, 0, numV); } g = freeGraph(g); g = NULL; } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfsquack.c GraphAM.c Quack.c && ./a.out < input_path.txt
2.2 通过 recursion 来实现
- 不断的递归遍历
- 直到所有的点都已经访问过停止
dfsR.c
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 void dfsR(Graph g, Vertex v, int numV, int *order, int *visited); int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void dfs(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; dfsR(g, v, numV, &order, visited); showArray(visited, numV); free(visited); return; } void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) { visited[v] = *order; *order = *order+1; Vertex w; for (w=0; w < numV; w++) { if (isEdge(g, newE(v,w)) && visited[w]==UNVISITED) { dfsR(g, w, numV, order, visited); } } return; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { showGraph(g); dfs(g, 0, numV); } g = freeGraph(g); g = NULL; } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfsR.c GraphAM.c Quack.c && ./a.out < input_R.txt
3. BFS:Breadth First Search,广度优先搜索
通过 queue 来实现(先进先出)
- 算法与 DFS 一致,只是将 stack 换成了 queue
- 将邻接点都 push 进去后,再 pop
- 由于是 queue,因此会依次先将邻接点进行 pop
- 然后再 pop 邻接点的所有邻接点
- 相当于树的分层遍历
- 效果与DFS类似,只是 stack 变成 queue
bfsquack.c
// bfsquack.c: traverse a graph using bfs and a stack implementation #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" void bfs(Graph, Vertex, int); #define WHITESPACE 100 int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void bfs(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *v = malloc(numV * sizeof(int)); if (v == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } int i; for (i=0; i<numV; i++) { v[i] = UNVISITED; } return v; } void showArray(int *v, int numV) { int i; printf("Visited: {"); for (i=0; i<numV; i++) { printf("%d", v[i]); if (i <= numV-2) { printf(", "); } } printf("}\n"); return; } int *visited = mallocArray(numV); Quack s = createQuack(); qush(v, s); showQuack(s); int order = 0; while (!isEmptyQuack(s)) { v = pop(s); if (visited[v] == UNVISITED) { showArray(visited, numV); //printf("visited[%d]=%d\n", v, order); visited[v] = order++; Vertex w; for (w=0; w<=numV-1; w++) { if (isEdge(g, newE(v,w))) { qush (w, s); } } } showQuack(s); } showArray(visited, numV); free(visited); return; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { showGraph(g); bfs(g, 0, numV); } g = freeGraph(g); g = NULL; } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc bfsquack.c GraphAM.c Quack.c && ./a.out < input_path.txt // clear && gcc dfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_path.txt
4. 其他应用
4.1 非连通图遍历 - DFS recursion 实现
- 通过遍历 visited 来判断是否遍历完全
- 遇到没便利的点,就从此为起点继续遍历
- 先假设已经全部遍历,然后对 visited 数组进行遍历,如果存在未访问的,则比较 finished=0,从此顶点继续执行DFS
- 以此类推,直到全部访问为止
dfsR_multi.c
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 void dfsR(Graph g, Vertex v, int numV, int *order, int *visited); int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void dfsDisc(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; Vertex newv = v; // this is the starting vertex int finished = 0; while (!finished) { // as long as there are vertices dfsR(g, newv, numV, &order, visited); Vertex w; finished = 1; // assume all vertices visited for (w = 0; w < numV && finished; w++) { // look for a new vertex if (visited[w] == UNVISITED) { finished = 0; // found an unvisited vertex newv = w; } } } showArray(visited, numV); free(visited); return; } void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) { visited[v] = *order; *order = *order+1; Vertex w; for (w=0; w < numV; w++) { if (isEdge(g, newE(v,w)) && visited[w]==UNVISITED) { dfsR(g, w, numV, order, visited); } } return; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { showGraph(g); dfsDisc(g, 0, numV); } g = freeGraph(g); g = NULL; } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfsR_multi.c GraphAM.c Quack.c && ./a.out < input_multi.txt
4.2 最短路径查找 - BFS queue 实现
- BFS 在遍历的过程中就是按层扫描
- 因此第一次扫描到 goal vertex 的时候就是最短的路径
- 类似 tree 的形式
- 因此每个 vertex 都有唯一的 parent
- 通过回溯 parent 可以将最短路径的顶点输出
- 每个点都有唯一的parent,因此每读入一个点就获取其parent的值
- 最后到达goal的时候,通过回溯parent可以将路径获取
bfs_path_searching.c
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void searchPath(Graph g, Vertex start, Vertex goal, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } void printPath(int parent[], int numV, Vertex v) { printf("%d", v); if (0<=v && v<numV) { Vertex p = parent[v]; while (p != UNVISITED) { printf("<--%d", p); p = parent[p]; } } else { fprintf(stderr, "printPath: illegal vertex in parent[]\n"); } } int *visited = mallocArray(numV); int *parent = mallocArray(numV); // need extra array to store parents Quack q = createQuack(); qush(start, q); int order = 0; visited[start] = order++; int found = 0; while (!isEmptyQuack(q) && !found) { Vertex x = pop(q); Vertex y; for (y = 0; y<numV && !found; y++) { if (isEdge(g, newE(x,y))) { // for adjacent vertex y ... if (visited[y]==UNVISITED) { // ... if y is unvisited ... qush(y, q); // ... queue y visited[y] = order++; // y is now visited parent[y] = x; // y's parent is x if (y == goal) { // if y is the goal ... found = 1; // ... SUCCESS! now get out } } } } } if (found) { printf("SHORTEST path from %d to %d is ", start, goal); printPath(parent, numV, goal); putchar('\n'); } else { printf("no path found\n"); } free(visited); free(parent); makeEmptyQuack(q); return; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { searchPath(g, 2, 3, numV); } } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc bfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_bow.txt // clear && gcc bfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_propsbows.txt // clear && gcc bfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_multi.txt // no path found // clear && gcc bfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_path.txt
4.3 查找循环路径 - DFS recursion 实现
- 循环遍历直到 visited 被访问过为止
- (v, w),就是按照 edge 的方式进行查找,但是不能找到过来的顶点,即fromv
- 除此之外,如果遇到了访问的顶点就是发现了循环
dfs_cycle.c
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 void dfsR(Graph g, Vertex v, int numV, int *order, int *visited); int hasCycle(Graph g, int numV, Vertex fromv, Vertex v, int *order, int *visited); int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void dfs(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; dfsR(g, v, numV, &order, visited); showArray(visited, numV); free(visited); return; } void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) { visited[v] = *order; *order = *order+1; Vertex w; for (w=0; w < numV; w++) { if (isEdge(g, newE(v,w)) && visited[w]==UNVISITED) { dfsR(g, w, numV, order, visited); } } return; } void searchForCycle(Graph g, int v, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; if (hasCycle(g, numV, v, v, &order, visited)) { printf("found a cycle\n"); } else { printf("no cycle found\n"); } showArray(visited, numV); free(visited); return; } int hasCycle(Graph g, int numV, Vertex fromv, Vertex v, int *order, int *visited) { int retval = 0; visited[v] = *order; *order = *order+1; Vertex w; for (w=0; w<numV && !retval; w++) { if (isEdge(g, newE(v,w))) { if (visited[w]==UNVISITED) { printf("traverse edge %d-%d\n", v, w); retval = hasCycle(g, numV, v, w, order, visited); } else { if (w != fromv) { // exclude the vertex we've just come from printf("traverse edge %d-%d\n", v, w); retval = 1; } } } } return retval; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { searchForCycle(g, 0, numV); } } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfs_cycle.c GraphAM.c Quack.c && ./a.out < input_no_cycle.txt // clear && gcc dfs_cycle.c GraphAM.c Quack.c && ./a.out < input_cycle.txt
4.4 Depth-First Search: Eulerian cycles
An Eulerian path is a path that includes every edge exactly once
A path may include many visits to the same vertex.
An Eulerian cycle is an Eulerian path that starts and ends on the same vertex
- 沿着一条道儿一直走,走过的路就删掉
- 因此不能走回头路
- 面对几条路的时候,选择最大顶点的(可以任意定规则)
- 如果能回到初始点,就是 cycle 了
- 访问过的路线都给删除掉,然后一条道走到黑,如果能够回到原点则为找到循环
dfs_EulerCycle.c
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 void dfsR(Graph g, Vertex v, int numV, int *order, int *visited); Vertex getAdjacent(Graph g, int numV, Vertex v); int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void dfs(Graph g, Vertex v, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; dfsR(g, v, numV, &order, visited); showArray(visited, numV); free(visited); return; } void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) { visited[v] = *order; *order = *order+1; Vertex w; for (w=0; w < numV; w++) { if (isEdge(g, newE(v,w)) && visited[w]==UNVISITED) { dfsR(g, w, numV, order, visited); } } return; } void findEulerCycle(Graph g, int numV, Vertex startv) { Quack s = createQuack(); printf("Eulerian cycle: \n"); push(startv, s); printf("push %d\n", startv); while (!isEmptyQuack(s)) { Vertex v = pop(s); // v is the top of stack vertex and ... push(v, s); // ... the stack has not changed Vertex w; if ((w = getAdjacent(g, numV, v)) >= 0) { push(w, s); // push a neighbour of v onto stack printf("push %d and remove %d-%d\n", w, v, w); removeE(g, newE(v, w)); // remove edge to neighbour } else { w = pop(s); printf("pop ------------------------ %d\n", w); } } putchar('\n'); } Vertex getAdjacent(Graph g, int numV, Vertex v) { // returns the Largest Adjacent Vertex if it exists, else -1 Vertex w; Vertex lav = -1; // the adjacent vertex for (w=numV-1; w>=0 && lav==-1; w--) { Edge e = newE(v, w); if (isEdge(g, e)) { lav = w; } } return lav; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { findEulerCycle(g, numV, 0); } } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfs_EulerCycle.c GraphAM.c Quack.c && ./a.out < input_box.txt // clear && gcc dfs_EulerCycle.c GraphAM.c Quack.c && ./a.out < input_bow.txt // clear && gcc dfs_EulerCycle.c GraphAM.c Quack.c && ./a.out < input_propbows.txt
4.5 路径查找
- 通过 dfs 进行查找
- 从起始顶点不停的向下递归,直到两个顶点重合位置
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "Graph.h" #include "Quack.h" #define UNVISITED -1 #define WHITESPACE 100 void dfsR(Graph g, Vertex v, int numV, int *order, int *visited); int isPath(Graph g, Vertex v, Vertex goalv, int numV, int *order, int *visited); int readNumV(void) { // returns the number of vertices numV or -1 int numV; char w[WHITESPACE]; scanf("%[ \t\n]s", w); // skip leading whitespace if ((getchar() != '#') || (scanf("%d", &numV) != 1)) { fprintf(stderr, "missing number (of vertices)\n"); return -1; } return numV; } int readGraph(int numV, Graph g) { // reads number-number pairs until EOF int success = true; // returns true if no error int v1, v2; while (scanf("%d %d", &v1, &v2) != EOF && success) { if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) { fprintf(stderr, "unable to read edge\n"); success = false; } else { insertE(g, newE(v1, v2)); } } return success; } void searchForPath(Graph g, int v, int goalv, int numV) { int *mallocArray(int numV) { int *array = malloc(numV * sizeof(int));// l if (array == NULL) { // o fprintf(stderr, "Out of memory\n"); // c exit(1); // a } // l int i; // f for (i=0; i<numV; i++) { // u array[i] = UNVISITED; // n } // c return array; // t } void showArray(int *array, int numV) { int i; // l printf("Visited: {"); // o for (i=0; i<numV; i++) { // c printf("%d", array[i]); // a if (i <= numV-2) { // l printf(", "); // f } // u } // n printf("}\n"); // c return; // t } int *visited = mallocArray(numV); int order = 0; if (isPath(g, v, goalv, numV, &order, visited)) { printf("found a path\n"); } else { printf("no path found\n"); } showArray(visited, numV); free(visited); return; } int isPath(Graph g, Vertex v, Vertex goalv, int numV, int *order, int *visited) { int found = 0; visited[v] = *order; *order = *order+1; if (v == goalv) { found = 1; } else { Vertex w; for (w=0; w < numV && !found; w++) { if (isEdge(g, newE(v,w))) { if (visited[w] == UNVISITED) { found = isPath(g, w, goalv, numV, order, visited); printf("path %d-%d\n", w, v); } } } } return found; } int main (void) { int numV; if ((numV = readNumV()) >= 0) { Graph g = newGraph(numV); if (readGraph(numV, g)) { searchForPath(g, 0, 6, numV); } } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } // clear && gcc dfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_bow.txt // clear && gcc dfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_propsbows.txt // clear && gcc dfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_multi.txt // no path found // clear && gcc dfs_path_searching.c GraphAM.c Quack.c && ./a.out < input_path.txt