Depth-first search is a general technique to explore a graph from a certain start vertex $s$. We imagine the mythical Greek hero Theseus starting to walk from $s$, carrying behind him a thread that his lover Ariadne has given him. When at a vertex $u$, Theseus progresses to a neighbor $v$, further unfurling the thread. He marks every visited vertex with chalk to remember where he has already been. When running in a dead end (either no outgoing edges or all outgoing edges go to vertices already carrying a chalk mark), he goes back where he came from, furling up the thread. When coming back to a vertex $u$, he proceeds to the next neighbor $v$ that he has not already visitied. So he additionally writes down, at each vertex $u$, the number of out-edges from $u$ he has already taken (this part is missing in the Greek legend, as far as I know). When he is back to $s$ and $s$ itself has no further unexplored out-neighbors, he completely furls up the rope and terminates. He has explored all vertices reachable from $s$.
Here is my python code:
debug_mode = Truedef debug_print(s):if debug_mode:print(s)def dfs(start_vertex, graph):visited = {} # the set of vertices that are or have been on the stacknumber_of_visited_out_edges = {} # for each vertex u, the number of outgoing edges (u,v) we have looked atfor u in graph:number_of_visited_out_edges[u] = 0visited[u] = Falsestack = [start_vertex] # this is a path starting at start_vertexvisited[start_vertex] = Truewhile (len(stack) > 0):u = stack[-1] # top element of the stacki = number_of_visited_out_edges[u] # what is the next unexplored edge out of u?if i == len(graph[u]): # then all out-edges of u have already been exploredstack.pop() # remove u from stack, i.e., backtrackdebug_print(f"popping {u} from the stack")else:v = graph[u][i] # edge (u,v) is the next unexplored out-edge of unumber_of_visited_out_edges[u] += 1debug_print(f"taking edge {(u,v)}")if not visited[v]:stack.append(v) # advancing the depth-first searchvisited[v] = Truedebug_print(f"pusing {v} onto stack")else:debug_print(f"{v} already visited")
The complete program that reads the graph in "ICPC format" plus a start vertex is in generic-dfs-digraph.py. Save it an use it with input digraph-6-8-start-at-1.txt:
generic-dfs-digraph < digraph-6-8-start-at-1.txt[lots of output]
Reachability
Reachability is the problem of determining which vertices are reachable from a given start vertex. For example, in the graph
the vertices reachable from start vertex $4$ are (in ascending order): $2, 3, 4, 6$. The other vertices $1$ and $5$ are not reachable.
Problem (Vanilla Reachability) Given a directed graph and a start vertices $s$, determine the set of vertices that can be reached from $s$.
Input: The first line contains two integers $n$ and $m$, the number of vertices and edges of the graph. The next $m$ lines each contain two integers $u$ and $v$, indicating that $(u,v)$ is an edge. The final line contains a single integer $s$, the start node.
Output: A total of $n$ lines, line $i$ ($1 \leq i \leq n$) containing the words "$i$ reachable" or "$i$ not reachable".
6 8
1 2
1 6
2 3
5 2
3 4
4 2
5 3
4 6
2
1 not reachable
2 reachable
3 reachable
4 reachable
5 not reachable
6 reachable