Understanding Depth-First Search

Depth-First Search (DFS) is a traversal technique used in graph data structures that explores each branch as far as possible before backtracking. This technique is often implemented using a stack data structure or through recursion.

Process

  1. Initialization: Initialize a stack (or use recursion) and push the starting vertex. Mark the starting vertex as visited.

  2. Traversal: While the stack is not empty (or during the recursion), perform the following steps:

    1. Pop the top vertex from the stack (or process the current vertex in recursion).

    2. Process the vertex (e.g., print its value, check a condition, etc.).

    3. Push all the adjacent (unvisited) vertices of the popped vertex onto the stack and mark them as visited.

  3. Termination: The process terminates when the stack becomes empty, indicating that all vertices reachable from the starting vertex have been processed.

When to Use

  1. Path Finding: To find a path between two vertices.

  2. Connected Components: To check if a graph is connected or to find all the connected components in an undirected graph.

  3. Topological Sorting: Order vertices in a Directed Acyclic Graph (DAG).

  4. Cycle Detection: To detect cycles in both directed and undirected graphs.

  5. Solving Puzzles: Useful in scenarios like mazes, puzzles, and games where all possible moves must be explored.

Time Complexity

The DFS traversal has a time complexity of O(V+E), where V is the number of vertices and E is the number of edges in the graph. This is because each vertex and each edge is processed exactly once.

Implementation using Recursion

// TC: O(V+E)
// SC: O(V)

import java.util.LinkedList;

class Graph {
    private int V;
    private LinkedList<Integer> adj[];

    @SuppressWarnings({ "unchecked", "rawtypes" })
    Graph(int v) {
        V = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; i++) {
            adj[i] = new LinkedList();
        }
    }

    void addEdge(int v, int w) {
        adj[v].add(w);
    }

    void DFS(int s) {
        boolean visited[] = new boolean[V];
        DFSUtil(s, visited);
    }

    void DFSUtil(int s, boolean visited[]) {
        visited[s] = true;
        System.out.print(s + " ");
        for (int n : adj[s]) {
            if (!visited[n]) {
                DFSUtil(n, visited);
            }
        }
    }

    public static void main(String args[]) {
        Graph g = new Graph(4);
        g.addEdge(0, 1);
        g.addEdge(0, 2);
        g.addEdge(1, 2);
        g.addEdge(2, 0);
        g.addEdge(2, 3);
        g.addEdge(3, 3);
        g.DFS(2);
    }
}

Implementation using Stack

// TC: O(V+E)
// SC: O(V)

import java.util.LinkedList;
import java.util.Stack;

class Graph {
    private int V;
    private LinkedList<Integer> adj[];

    @SuppressWarnings({ "unchecked", "rawtypes" })
    Graph(int v) {
        V = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; i++) {
            adj[i] = new LinkedList();
        }
    }

    void addEdge(int v, int w) {
        adj[v].add(w);
    }

    void DFS(int s) {
        boolean visited[] = new boolean[V];
        Stack<Integer> stack = new Stack<>();
        stack.push(s);
        while (!stack.isEmpty()) {
            int node = stack.pop();
            if (!visited[node]) {
                visited[node] = true;
                System.out.print(node + " ");
                for (int i = adj[node].size() - 1; i >= 0; i--) {
                    int n = adj[node].get(i);
                    if (!visited[n]) {
                        stack.push(n);
                    }
                }
            }
        }
    }

    public static void main(String args[]) {
        Graph g = new Graph(4);
        g.addEdge(0, 1);
        g.addEdge(0, 2);
        g.addEdge(1, 2);
        g.addEdge(2, 0);
        g.addEdge(2, 3);
        g.addEdge(3, 3);
        g.DFS(2);
    }
}

Thank you for reading!

You can support me by buying me a book.