This article will introduce depth-first search for graphs and implement topological ordering based on depth-first search (topological sorting is applied to a direction-free graph, described in detail below).
1. Depth-first traversal of graphs to solve problems
The depth-first search of the graph is similar to the depth-first search of the tree, but the depth-first search to solve a problem, that is, the vertex of the repeated access, assuming that there is a loop a-b-c-a in the graph, then the vertex a is expanded after the B, the B is expanded after the C, then the C is expanded to get A, Then A is repeatedly visited ...
This is obviously wrong! We need a state variable to record the state in which a vertex is accessed and expanded. In the introduction to the algorithm, the author uses 3 colors to mark this: the white--vertex has not been accessed, the gray--vertex has been accessed but its child nodes have not been accessed, the black--vertex has been visited and its child nodes have been accessed.
2. Using the stack to realize the depth-first traversal of graphs
In the introduction to algorithms, the author uses recursion to implement depth-first traversal. Although recursion is more concise and easy to understand, I don't recommend doing this in real-world projects unless you have a smaller problem size. Otherwise, recursion will leave your program in a hopeless process. Just as using a queue to achieve breadth-first search (you can see the breadth-first traversal of my previous blog graph), I'll use stacks to implement a depth-first search.
Here's an example of how to use a stack to implement a depth-first traversal. Suppose there is a diagram below.
First, we initialize an empty stack and then put all the vertices into the stack, at which point all the vertices are not accessed, so all the vertices are set to white, as shown below.
After the initialization is complete, vertex 5 is at the bottom of the stack, vertex 1 is at the top of the stack, and then we go into the loop operation, first out of the stack a vertex, the vertex is vertex 1, because the vertex 1 is white, that is not visited, so the vertex 1 is gray, and re-enter the stack (because the following is to record the node's sub-nodes are accessed order), and then expand all vertex 1 vertex, only vertex 2, because the vertex 2 is white is not accessed, so the vertex 2 into the stack, as shown in.
Then we continue the loop, out of the stack a vertex, that is, Vertex 2, because the vertex 2 is white, so it is gray, and then re-enter the stack, and then expand to get Vertex 4 and vertex 5, because the vertex 4 and vertex 5 are white, so they are all in the stack. As shown in.
Then continue the loop, out of the stack a vertex, that is, Vertex 5, because the vertex 5 is white, so it is gray, and re-enter the stack, and then expand Vertex 5, found that vertex 5 has no child nodes, as shown in.
Then continue the loop, out of the stack vertex 5, when the vertex 5 is gray, the vertex 5 of the child node has been visited, so the vertex 5 is set to black, and its order is marked 1, indicating that he is the first node to be expanded, this record will be applied to the topological sorting. The status of this stack is as follows.
Continue the loop, out of the stack Vertex 4, because it is white, so the vertex 4 is gray and re-enter the stack, and then expand Vertex 4 to get vertex 5, because the vertex 5 is black, so do not put it into the stack. Next proceed to the stack Vertex 4, find that vertex 4 is gray, and as before, the vertex 4 is set to black and its order is recorded as 2. The stack at this point is as shown.
Well, the work of the back loop is similar to the above, and the vertices 2, 1, and then the stack are set to black until Vertex 3 expands to find no white sub-nodes, and it is also set to black. Then to vertex 4 and vertex 5, because it is black, directly out of the stack without any processing. At this point the stack is empty and the traversal ends.
The input diagram required by our program is represented in the form of an adjacency table, and the definition of the graph and vertex and adjacency table is given below.
typedef enum vertexcolor{vertex_white = 0,//not searched vertex_black = 1,//child nodes are searched Vertex_gray = 2//child nodes are being searched} Vertexcolor;typ edef struct Gnode{int number;//vertex number struct gnode *next;} gnode;typedef struct vertex{int number;int f; Vertexcolor color;//Search Process flag search status struct Vertex *p;} vertex;typedef struct Graph{gnode *linktable; Vertex *vertex;int Vertexnum;} Graph;
Vertexcolor is the color enumerator definition for the vertex. Gnode is an element definition of an adjacency table that records the numbering of vertices. An adjacency table is essentially an array of pointers, each of which is a pointer to the first element of a linked list. Vertex is the data structure of a vertex whose attribute number is a vertex, F is the order in which it is accessed, color is the status identification color, and p points to the predecessor of the vertex in the depth-first traversal tree that was obtained after the search was completed. Graph is a data structure definition of a graph that contains a set of top points, arranged in ascending order by number, and linktable is an adjacency table.
The following is a program for depth-first search implemented using stacks.
/*** depth First search, The node number required to input figure G starts from 1 */void searchbydepthfirst (graph *g) {int vertexnum = g->vertexnum; Stack *stack = Initstack (); Vertex *vs = g->vertex; Gnode *linktable = G->linktable;int order = 0;for (int i = 0; i < Vertexnum; i++) {Vertex *v = vs + I;v->color = V Ertex_white;v->p = Null;push (&stack, v->number);} while (!isempty (stack)) {int number = pop (&stack); Vertex *u = vs + Number-1;if (U->color = = Vertex_white) {//Start searching for the node's sub-nodes U->color = Vertex_gray;push (&stack, num ber);} else if (U->color = = Vertex_gray) {//The node's sub-nodes have been searched U->color = Vertex_black;u->f = order++;continue;} Else{continue;} Gnode *links = linktable + number-1;links = links->next;while (links! = NULL) {//Expand sub-node merge stack vertex *v = vs + Links->n Umber-1;if (V->color = = vertex_white) {v->p = U;push (&stack, links->number);} Links = Links->next;}}}
The program first initializes all vertices to white and merges into the stack, and then begins the loop. In the loop, each time a node out of the stack must first determine its color, if it is gray, mark its completion of the order of access and marked black, if it is black, do not do any processing, if it is white, then gray and re-enter the stack, and then expand all its child nodes and the white sub-nodes into the stack, And note the precursors of these white vertices. When the stack is empty, the loop ends.
The operation of the stack can refer to other materials, not detailed here, the following gives a simple implementation.
typedef struct STACK {int value;struct stack *pre;} Stack;
Above is the structure definition of the stack.
stack* Initstack () {Stack *s = (stack *) malloc (sizeof (stack)); s->pre = Null;return s;} void push (stack **s, int value) {Stack *n = (stack *) malloc (sizeof (stack)); n->pre = *s;n->value = Value;*s = n;} int pop (Stack **s) {if ((*s)->pre = = NULL) {return int_max;} int value = (*s)->value; Stack *pre = (*s)->pre;free (*s); *s = Pre;return value;} int IsEmpty (Stack *s) {if (S->pre = = NULL) {return 1;} return 0;} void Destroystack (Stack **s) {while (*s! = NULL) {Stack *pre = (*s)->pre;free (*s); *s = Pre;}}
Above is the method of the stack, in order to initialize an empty stack, into the stack, out of the stack, to determine whether it is an empty stack, destroy the stack.
Here is an example code and a running result that applies the above-mentioned depth-first traversal method.
Graph Graph; Graph. Vertexnum = 5; Vertex V[5]; Vertex v1; V1.number = 1; V1.P = NULL; V[0] = v1; Vertex v2; V2.number = 2; V2.P = NULL; V[1] = v2; Vertex v3; V3.number = 3; V3.P = NULL; V[2] = v3; Vertex v4; V4.number = 4; V4.P = NULL; V[3] = v4; Vertex v5; V5.number = 5; V5.P = NULL; V[4] = V5;graph.vertex = v; Gnode Nodes[5]; Gnode N1; N1.number = 1; Gnode N2; N2.number = 2; Gnode N3; N3.number = 3; Gnode N4; N4.number = 4; Gnode N5; N5.number = 5; Gnode y; Y.number = 5; Gnode e; E.number = 2; Gnode F; F.number = 5; Gnode G; G.number = 2; Gnode h; H.number = 4; N1.next = &e; E.next = Null;n2.next = &h; H.next = &f; F.next = Null;n3.next = &g; G.next = Null;n4.next = &y; Y.next = Null;n5.next = Null;nodes[0] = n1;nodes[1] = n2;nodes[2] = n3;nodes[3] = n4;nodes[4] = n5;graph. linktable = Nodes;searchbydepthfirst (&graph);p rintf ("\ n");p Rintpath (&graph, 2);
The results of the operation are as follows.
The example code above gives a depth-first traversal of the graph given earlier in this article, outputting the precursor subtree of Vertex 2, resulting in the above result, meaning that vertex 2 has a precursor vertex of 3.
3. Topological ordering of graphs
The topological ordering of graphs can be implemented by applying depth-first traversal.
First, explain what a topological sort is. Topological sorting refers to arranging the graphs in a sequential order, which means that in the sort results, the preceding vertices may have a simple path leading to the back vertex, and the latter vertex is definitely not a simple path leading to the previous vertex. Topological sorting is applied to a direction-free graph. An example of topological ordering is given below.
Or this diagram, which is also a directed, non-circular graph, the result of the topological ordering is definitely a root vertex, that is, there is no other vertex pointing at him. The topological ordering results for this graph are 1, 3, 2, 4, 5, or 1, 3, 2, 5, 4, and so on, and the order of the vertices that do not point to the relationship is indeterminate. We can think of the topological sort of diagram as a schedule, vertex 1 for washing, Vertex 3 for brushing, Vertex 2 for breakfast, vertex 4 for wearing pants, vertex 5 for dress, so the essence of topological sorting is organized according to the order in which events should occur.
A feature of depth-first traversal of graphs is that when a vertex's sub-node is accessed, the vertex ends the access and begins to backtrack to the other child nodes of its parent node. This means that the end access time of a vertex has a relationship with the end access time of its child nodes, which is exactly the opposite of the topological order! To put it simply, in depth-first traversal, vertex A has to end the access by its sub-nodes B, C, D ... have been accessed, and in topology sequencing, event A completes after event B, C, D ... Before it can proceed. This is why the above depth-first search records the order in which the nodes are accessed, because this is the order of topological sorting!
The following is a program for topology sequencing based on the depth-first search of graphs.
/*** has a topological sort of */void topologysort (graph *g, int **order, int *n) {Searchbydepthfirst (g); *n = G->vertexnum;*order = (in T *) malloc (sizeof (int) * *n); for (int i = 0; i < *n; i++) {(*order) [*n-1-G->VERTEX[I].F] = i + 1;}}
Similarly, the above-mentioned program implementation of the topological ordering requirements of the graph's vertex number is also starting from 1.
In fact, there is another way to implement topological sequencing, which is to remove all root vertices and the edges that are attached to them from the root vertex. A new root vertex is generated, and then continues to repeat, knowing that all vertices are deleted.
4. Summary
This paper introduces how to use the stack to realize the depth-first search of graphs, and realizes the topological sort based on the depth-first search. All of its time complexity is O (n). The complete program can refer to my GitHub project data structure and algorithm
This project has been introduced in this blog and did not introduce and will be introduced in the "Introduction to the algorithm" part of the main data structure and algorithm C implementation, interested can fork or star Oh ~ because I am still studying "introduction to the algorithm", so this project will continue to update oh ~ Everyone together to learn ~
Depth-first search and topological sequencing of graphs