DEV Community

Khushi
Khushi

Posted on • Edited on

Depth-First Search

🔁Quick BFS Recap

  • In Breadth-First Search (BFS), you explore the graph level by level.
  • You visit all immediate neighbors first, then move on to their neighbors, and so on.
  • Uses a queue (FIFO)
  • Best for shortest path in unweighted graphs
  • Real-life: friend suggestions, web crawling, peer-to-peer networks

Now, Let’s Dive into DFS – Depth-First Search

  • No more level-by-level. It’s time to go DEEP!
  • In DFS, you pick a path and go as deep as possible, only backtracking when there’s nowhere left to go.

Let's Imagine this,

You enter a maze and just keep walking forward till you hit a dead-end, then backtrack and explore the next path.

It Uses recursion or a stack and best for the Exploring complete paths, Detecting cycles, Maze solving, Topological sort.

What is DFS?
Depth-First Search (DFS) is a classic graph traversal algorithm.
Instead of exploring layer by layer like BFS, it dives deep into each branch of a graph or tree before backtracking.

How It Works:

1.Start from a source node

  • Choose a node to begin with (say, node 0).
  • This will be the root of your traversal.

2.Use a stack (or recursion) to track the path

  • DFS relies on a stack data structure.
  • You can either explicitly use a stack or let recursion handle it for you (since function calls internally use a call stack).

3.Maintain a visited[] array

  • To avoid infinite loops (especially in cyclic graphs), keep track of nodes you’ve already visited.
  • This ensures each node is processed only once.

4.Process nodes deeply before backtracking

  • Visit the current node and mark it as visited.
  • Then, move to its first unvisited neighbor.
  • Continue this process until there are no unvisited neighbors left.
  • When stuck, backtrack to the most recent node that still has unvisited neighbors, and repeat.

🧠 Graph Structure (Adjacency List based on the image):
From the graph:
1: [2, 3]
2: [1, 4]
3: [1, 5]
4: [2, 5]
5: [3, 4]

We’ll assume:
It’s an undirected graph.

Step 1

  • Start DFS at 1.
  • Mark 1 as visited → Visited = [1].
  • Explore first neighbor → 2.

Step 2

  • At node 2.
  • Mark 2 as visited → Visited = [1, 2].
  • Explore first neighbor → 4.

Step 3

  • At node 4.
  • Mark 4 as visited → Visited = [1, 2, 4].
  • Neighbors of 4 = (2, 5).
    • 2 already visited → skip.
    • Move to neighbor 5.

Step 4

  • At node 5.
  • Mark 5 as visited → Visited = [1, 2, 4, 5].
  • Neighbors of 5 = (3, 4).
    • 4 already visited → skip.
    • Next is 3.

Step 5

  • At node 3.
  • Mark 3 as visited → Visited = [1, 2, 4, 5, 3].
  • Neighbors of 3 = (1, 5). Both already visited → backtrack.

Step-by-Step (Recursive DFS)
Start at 1, mark visited → dfs = [1]
Go to neighbor 2 (first unvisited of 1) → dfs = [1, 2]
Go to neighbor 4 (from 2) → dfs = [1, 2, 4]
Go to neighbor 5 (from 4) → dfs = [1, 2, 4, 5]
Go to neighbor 3 (from 5) → dfs = [1, 2, 4, 5, 3]
All neighbors visited. Backtrack until all paths are done.

🔄 Final DFS Traversal Order:
1 → 2 → 4 → 5 → 3

DFS Code in C++

#include <iostream>
#include <vector>
using namespace std;

// Helper method to perform DFS recursively
void dfsHelper(int node, vector<int> adj[], vector<bool>& visited, vector<int>& dfs) {
    visited[node] = true;
    dfs.push_back(node);

    for (int neighbor : adj[node]) {
        if (!visited[neighbor]) {
            dfsHelper(neighbor, adj, visited, dfs);
        }
    }
}

// DFS function that calls the helper
vector<int> dfsOfGraph(int V, vector<int> adj[]) {
    vector<int> dfs;
    vector<bool> visited(V, false);
    dfsHelper(0, adj, visited, dfs); // starting from node 0
    return dfs;
}

int main() {
    int V = 5;
    vector<int> adj[V];


    // 1-2, 1-3, 2-4, 3-5, 4-5
    adj[0] = {1, 2};    // node 1 -> 2, 3
    adj[1] = {0, 3};    // node 2 -> 1, 4
    adj[2] = {0, 4};    // node 3 -> 1, 5
    adj[3] = {1, 4};    // node 4 -> 2, 5
    adj[4] = {2, 3};    // node 5 -> 3, 4

    vector<int> result = dfsOfGraph(V, adj);

    cout << "DFS Traversal: ";
    for (int node : result)
        cout << node + 1 << " ";  
    cout << endl;

    return 0;
}

Enter fullscreen mode Exit fullscreen mode

⏱ Time Complexity → O(V + E)

  • V (Vertices):

    • Every vertex is visited exactly once.
    • Visiting a node + marking as visited = O(1) each → overall O(V).
  • E (Edges):

    • For every node, DFS explores its adjacency list (all connected edges).
    • Across the whole graph, each edge is checked at most twice (once from each endpoint in an undirected graph, once in a directed graph).
    • Total edge exploration = O(E).

So, the total time = O(V + E).

Space Complexity → O(V)
DFS requires memory for:

  • Visited array → To keep track of visited nodes (size = V).
  • Recursion stack (or explicit stack) →
  • In the worst case (graph shaped like a linked list), recursion depth can go up to V.

So, maximum call stack size = O(V).

Top comments (0)