Depth-first search (DFS) is a graph traversal algorithm that explores as deep as possible along a branch before backtracking. It uses recursion or a stack to systematically visit all nodes of a graph or tree.
Key takeaways:
Depth-first search (DFS) explores as deep as possible into a graph along each branch before backtracking, making it suitable for exploration and connectivity problems.
DFS can be implemented:
Recursively (using the call stack)
Iteratively (using an explicit stack)
Common graph representations include:
Adjacency list: Space-efficient and widely used for sparse graphs.
Adjacency matrix: Simple but consumes more space, especially for large graphs.
DFS traversal order depends on the order of neighbors in the graph’s representation.
This variability can affect outcomes in applications like maze solving or graph searches.
DFS is versatile and forms the foundation for solving complex problems in AI, pathfinding, and graph analysis.
Depth-first search (DFS) is a fundamental graph traversal algorithm that explores nodes in a graph by moving as deep as possible along a branch before backtracking. DFS uses the “last in, first out” principle, often implemented via recursion or a stack. This makes it a powerful and straightforward tool for systematically visiting all vertices in a graph.
In essence, DFS helps discover paths and understand the structure of a graph or tree by following each path to its conclusion before exploring alternative routes.
Depth-first search (DFS) explores nodes in a graph by venturing as deep as possible along a branch before backtracking. It can be implemented using recursion (leveraging the call stack) or an explicit stack. Below is the step-by-step explanation of the DFS algorithm:
Initialization:
Represent the graph using an adjacency list or adjacency matrix.
Use a visited
array (or a set) to track previously explored nodes.
Start at a node:
Select a starting node (typically provided or chosen arbitrarily).
Mark as visited:
Mark the current node as visited to prevent reprocessing.
Traverse adjacent nodes:
For each adjacent (unvisited) node of the current node:
Recursively call DFS on the node (if using recursion).
Or push the node onto the stack (if using an iterative approach).
Backtrack:
In the recursive approach, backtracking occurs naturally as the call stack unwinds.
In the iterative approach, nodes are popped off the stack when all their neighbors have been processed.
Repeat until all nodes are visited:
If disconnected components exist, restart DFS from an unvisited node until all nodes are covered.
Here is the pseudocode for the depth-first search (DFS) algorithm, presented in both iterative and recursive forms:
function DFSRecursive(node, graph, visited):visited[node] = true // Mark the current node as visitedprint(node) // Print the visited node (optional step)for each neighbor in graph[node]:if not visited[neighbor]:DFSRecursive(neighbor, graph, visited) // Recursive call for unvisited neighbors
Steps:
Mark the current node as visited.
Recursively visit each unvisited neighbor of the current node.
function DFSIterative(start, graph):visited = array of false values of size graph.size // Initialize visited arraystack = empty stack // Create an empty stackstack.push(start) // Push the starting nodeprint("DFS Iterative Traversal:")while stack is not empty:node = stack.top()stack.pop()if not visited[node]:visited[node] = true // Mark the node as visitedprint(node) // Print the visited node (optional step)// Push all unvisited neighbors onto the stackfor each neighbor in graph[node]:if not visited[neighbor]:stack.push(neighbor)end whileend function
Steps:
Use an explicit stack to simulate recursion.
Push the starting node onto the stack.
While the stack is not empty, process the node at the top.
Push all unvisited neighbors of the current node onto the stack.
Let’s illustrate this with an example. Consider the following graph:
So the DFS traversal for this graph is:
Below is a complete implementation of DFS using both recursive and iterative approaches. The program accepts a graph as input, performs DFS traversal, and outputs the order in which the nodes are visited.
The traversal order can vary slightly depending on the graph representation and the stack’s behavior.
Recursive DFS:
#include <iostream>#include <vector>using namespace std;// Recursive DFS functionvoid DfsRecursive(int node, const vector<vector<int>>& graph, vector<bool>& visited) {visited[node] = true; // Mark the current node as visitedcout << node << " "; // Print the visited nodefor (int neighbor : graph[node]) {if (!visited[neighbor]) {DfsRecursive(neighbor, graph, visited); // Recursive call for unvisited neighbors}}}int main() {// Example graph represented as an adjacency listvector<vector<int>> graph = {{}, // Node 0 (not used){2, 3}, // Node 1 connects to nodes 2, 3{1, 4}, // Node 2 connects to nodes 1, 4{1, 4}, // Node 3 connects to nodes 1, 4{2, 3} // Node 4 connects to nodes 2, 3};cout << "DFS Recursive Traversal: ";vector<bool> visited(graph.size(), false);DfsRecursive(1, graph, visited); // Start DFS from node 1cout << endl;return 0;}
Explanation of the output:
Starting from node 1:
Visits node 1, then recursively visits its neighbors (2, then 4, then 3).
The traversal follows a “deep first” approach, visiting as far as possible along each branch before backtracking.
Iterative DFS:
#include <iostream>#include <vector>#include <stack>using namespace std;// Iterative DFS functionvoid DfsIterative(int start, const vector<vector<int>>& graph) {vector<bool> visited(graph.size(), false); // Initialize visited arraystack<int> stk; // Create a stack for DFSstk.push(start); // Push the starting nodewhile (!stk.empty()) {int node = stk.top();stk.pop();if (!visited[node]) {visited[node] = true; // Mark the node as visitedcout << node << " "; // Print the visited node// Push all unvisited neighbors onto the stackfor (int neighbor : graph[node]) {if (!visited[neighbor]) {stk.push(neighbor);}}}}}int main() {// Example graph represented as an adjacency listvector<vector<int>> graph = {{}, // Node 0 (not used){2, 3}, // Node 1 connects to nodes 2, 3{1, 4}, // Node 2 connects to nodes 1, 4{1, 4}, // Node 3 connects to nodes 1, 4{2, 3} // Node 4 connects to nodes 2, 3};cout << "DFS Iterative Traversal: ";DfsIterative(1, graph); // Start DFS from node 1cout << endl;return 0;}
Explanation of the output:
Starting from node 1:
Uses a stack to simulate recursion.
Pushes neighbors of the current node onto the stack in reverse order (to mimic the recursive approach’s behavior).
Visits nodes in the order determined by the stack (Last in, first out behavior).
This program demonstrates how DFS explores the graph, making it suitable for applications such as connected component detection, cycle detection, and more.
Recursive DFS:
Time Complexity:
Space Complexity:
Iterative DFS:
Time Complexity:
Space Complexity:
Here’s a detailed comparison with a focus on how they affect the performance of depth-first search (DFS):
Aspect | Adjacency List | Adjacency Matrices |
Structure | List of neighbors for each vertex | V×V matrix where cell (i, j) represents an edge |
Space Complexity | O(V+E), efficient for sparse graphs | O(V^2), inefficient for sparse graphs |
Neighbour Lookup | Only iterates through connected neighbors | Scans the entire row for connected neighbors |
DFS Performance | O(V+E), efficient for sparse graphs | O(V^2) for sparse graphs; more competitive for dense graphs |
Advantages |
|
|
Disadvantages |
|
|
Best Use Case | Sparse graphs with fewer edges | Dense graphs with many edges |
Depth-first search is a fundamental algorithm in graph theory, and understanding it is essential for solving many graph-based problems. This Answer provided a simple and clear implementation of DFS in C++ and a visual explanation of how the algorithm works. By studying this implementation and the accompanying visuals, one can understand DFS and how to implement it in C++.
Quick Quiz!
Which data structure is typically used in the iterative implementation of depth-first search (DFS)?
Queue
Stack
Priority queue
Hash table
To effectively prepare for the coding interview, consider leveraging some of the most popular resource from Educative as follows:
Haven’t found what you were looking for? Contact Us
Free Resources