The DFS is the most fundamental search algorithm used to explore nodes and edges of a graph. It runs with a time complexity of $O(V+E)$ and is often used as a building block in other algorithms. By itself DFS isn't all that useful, but when augmented to perform other tasks such as count connected components, determine connectivity, or find bridges/articulation points then DFS really shines. #### General idea DFS plunges depth first into a graph without regard for which edge it takes next until it cannot go any further at which point it backtracks and continues. #### Pseudocode - Standard DFS ```python # Instantiate tracking variables n = 'number of nodes in the graph' g = 'adjacency list representing graph' visited = [False,...,False] # size n def dfs(at): if visited[at]: return visited[at] = True neighbors = g[at] for next in neighbors: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node) ``` #### Pseudocode - DFS to find number of connected components ```python # Instantiate tracking variables n = 'number of nodes in the graph' g = 'adjacency list representing graph' count = 0 # Tracks number of connected componets components = 'empty integer array' # size n, holds which component node belongs to visited = [False,...,False] # size n def find_components(): for i in range(0, n): if not visited[i]: count += 1 dfs(i) return (count, components) def dfs(at): visited[at] = True components[at] = count for next in g[at]: if not visited[next]: dfs(next) ``` #### Pseudocode- Stack vs recursive implementation ![](Screen%20Shot%202021-04-06%20at%209.14.28%20AM.png) * Note that both of the above implementations have a run time of $O(V + E)$. * A quick (intuitive) derivation of the above: * We can look at every single vertex, of which there are $V$. We never look at a vertex twice. * Then, based on the way that we scan our graph, we never go along the same edge twice. For instance, if we visited `A` and `B`, and there is an edge connecting them, we will never go back along that edge. ### Key Takeaways > DFS organizes vertices by entry/exit times, and edges into tree and back edges. This organization is what gives DFS its real power. > DFS on undirected graphs proves useful because it *organizes* the edges of the graph in a *very precise way* (/posts/see [Algorithms and Structures can create new Structures](Algorithms%20and%20Structures%20can%20create%20new%20Structures.md)) ### Examples/Use cases * [Topological-Sort](Topological-Sort.md) * Compute *strongly connected components* --- Date: 20211014 Links to: [Graph-Algorithms](Graph-Algorithms.md) [Topological-Sort](Topological-Sort.md) [Depth-First-Search vs. Breadth First Search](Depth-First-Search%20vs.%20Breadth%20First%20Search.md) Tags: References: * Algorithm Design Manual, Chapter 5: Graph Traversal * [DFS, Topological Sort (MIT)](https://www.youtube.com/watch?v=AfSk24UTFS8)