Graph Theory | Depth First Search | by Kelvin Jose | Towards Data Science

This is the third post of my series, Graph Theory: Go Hero. I highly recommend checking out the index for previous posts.
In Graph Theory, Depth First Search (DFS) is an important algorithm which plays a vital role in several graph included applications.
DFS is the most fundamental kind of algorithm we can use to explore the nodes and edges of a graph. It’s a form of traversal algorithm. The first and foremost fact about DFS is its engineering simplicity and understandability. DFS runs with a time complexity of O(V + E) where O stands for Big O, V for vertices and E for edges. Still, it’s coherence could be leveraged to other different applications such as detecting bridges and articulation points, counting connected components and estimating the connectivity.
As the name shows, a DFS algorithm visits the depth regardless of which node or edge comes after the current one, if we reach a dead end - from where we cannot move any further - we backtrack to the unvisited node and continues the process recursively until we visit all the possible nodes and edges. We have to make sure that we don’t visit the same node again.
For a given graph, multiple traversal options are available. The one that I have shown here starts at node 0 an continues like 1, 2, 3, ..., 9. The actual path would be 0 -1 -2 -1 -3 -1 - 4 - 5 - 6 - 7 - 6 - 5 - 8 - 5 - 4 - 9. Note, we backtrack to several nodes again figure out new unvisited paths.
# global variablesn = number_of_nodes_in_the_graphg = adjacency_listvisited = [false] * nfunction dfs(at):    if visited[at]:        return    visited[at] = true    neighbors = g[at]    for node in neighbors:        dfs(node)# start DFS at node 0start_node = 0dfs(start_node) 
We start defining the total number of nodes (n) in the graph. We will have an adjacency list - a structure used to store graphs in the memory - which includes every nodes and their corresponding adjacent connections. This is a map of nodes to lists of edges. We define a list of size n to denote whether the node is already visited or not. On initialization, the list would contain all false values, because we haven’t visited any nodes yet. As we visit each node, the corresponding positions would be replaced with true. A function has been defined with the name dfs() and has invoked at the end of pseudo code. We set the first node to be visited as 0. When the function dfs() is invoked, it checks whether the node is already visited or not, if no, we would replace the visited[node] which is false at the moment with true which marks the successful visit. Then we retrieve the neighbors of the just visited node and call the dfs() function on each and every neighbors, recursively.
Let’s discuss one of the primary use cases of DFS i.e. finding the connected components in a graph. A connected component of an undirected graph is a maximal set of nodes such that each pair of nodes is connected by a path.
Sometimes a graph might be divided into multiple components. Connected components form a partition of the set of graph vertices, meaning that connected components are non-empty, they are pairwise disjoints, and the union of connected components forms the set of all vertices. A vertex with no incident edges is also a standalone component. One way to identify components out of a graph is by coloring them. So, each component would have a distinct color associated with it. Coloring components is appealing to humans, but the relevant question is - how coloring is appealing to a machine i.e. a computer. In the case of machines, we could label each node of a component with similar ids, probably some integers. Like the coloring scheme, each component posses a unique id, then.
Let’s see how the above-described labeling is done. For ease, I have extracted a set of components from the above graph as below.
First, we have to number each node explicitly starting from 0 to n where n is the maximum number of nodes. The idea is to start DFS at every node which is not already visited and mark all reachable nodes as part of the same component. If we start at node 0, it’s already a component by itself because it has no incoming or outgoing connections and we label it with integer 1. Now, if we choose node 6 as the next node, we do DFS in the order 6 - 14 - 16 - 14 - 5 - 11 and we label all the nodes of this component as 2. We repeat the same process for every other components in the graph. The final result would look a bit more colorful as below.
# global variablesn = number_of_nodes_in_the_graphg = adjacency_listcount = 0component = []visited = [false] * nfunction find_components()    for(i=0; i<n: i++):        if !visited[i]:            count ++            dfs(i)    return countfunction dfs(at):    visited[at] = true    component[at] = count    for next in g[at]:        if !visited[next]:            dfs(next)
We first defined a number of global variables to store the number of nodes in the list, adjacency list, a counter to store number of components, a list to keep track of the component label and a list to see whether a node is already visited or not.
I have defined two functions named find_components() and dfs(). The first function loops through each node we have and ensures it’s visited. If no, the counter will be incremented by one to mark the existence of a new component and it invokes the dfs() function to do a Depth First Search on the component.
The dfs() function takes one parameter, i.e. the id of each node as argument. The first and foremost thing the dfs() function does is, it marks the current node visited and updates the component list with the counter. The counter value denotes the label of a component. All the nodes in the same component would have the same label. Then the function searches for any available neighbors in the adjacency list and invokes the same function recursively.
We can augment the DFS algorithm to:
Compute minimum spanning tree.
Detect and find cycles in the graph.
Determine bipartite graphs.
Find strongly connected components.
Sort the nodes.
Find bridges and articulation points.
That’s all about Depth First Search. Of Course, the next one would be a detail about Breadth First Search. I should definitely thank your time and appreciate your effort to come this far.