Contents

DFS starts at a given source node and explores as far as possible along each branch before backtracking. A visited set is maintained to avoid revisiting nodes, which would cause infinite loops in cyclic graphs.

Consider the below undirected graph:

Graph representation for DFS

Starting DFS from node 1, the traversal proceeds as follows:

DFS uses a Stack data structure (LIFO). In the recursive approach, the call stack acts as the stack implicitly.

The recursive approach is the most natural way to implement DFS. We pass a Set of visited nodes and for each unvisited neighbour of the current node, we recursively call DFS.

Algorithm steps:
  1. Mark the current node as visited.
  2. Process the current node (e.g. add to result list or print).
  3. For each neighbour of the current node, if it has not been visited yet, recursively call DFS on it.
import java.util.*; public class GraphDFS { // Adjacency list: node -> list of neighbouring nodes private Map<Integer, List<Integer>> adjacencyList = new HashMap<>(); public void addEdge(int from, int to) { adjacencyList.computeIfAbsent(from, k -> new ArrayList<>()).add(to); adjacencyList.computeIfAbsent(to, k -> new ArrayList<>()).add(from); // undirected } /** Public entry point for recursive DFS */ public void dfsRecursive(int start) { Set<Integer> visited = new HashSet<>(); dfsHelper(start, visited); } private void dfsHelper(int node, Set<Integer> visited) { visited.add(node); System.out.print(node + " "); // process the node List<Integer> neighbours = adjacencyList.getOrDefault(node, Collections.emptyList()); for (int neighbour : neighbours) { if (!visited.contains(neighbour)) { dfsHelper(neighbour, visited); } } } }
Example usage:
GraphDFS graph = new GraphDFS(); graph.addEdge(1, 2); graph.addEdge(1, 3); graph.addEdge(2, 4); graph.addEdge(3, 4); graph.addEdge(4, 5); graph.dfsRecursive(1); // Possible output: 1 2 4 3 5

The iterative approach uses an explicit Deque as a stack to simulate the recursive call stack. This avoids potential StackOverflowError for very deep or large graphs.

Algorithm steps:
  1. Push the start node onto the stack and mark it as visited.
  2. While the stack is not empty, pop a node from the top.
  3. Process the popped node.
  4. Push all unvisited neighbours of the popped node onto the stack, marking each as visited.
  5. Repeat until the stack is empty.
public void dfsIterative(int start) { Set<Integer> visited = new HashSet<>(); Deque<Integer> stack = new ArrayDeque<>(); stack.push(start); visited.add(start); while (!stack.isEmpty()) { int node = stack.pop(); System.out.print(node + " "); // process the node List<Integer> neighbours = adjacencyList.getOrDefault(node, Collections.emptyList()); for (int neighbour : neighbours) { if (!visited.contains(neighbour)) { visited.add(neighbour); stack.push(neighbour); } } } } Mark nodes visited when they are pushed onto the stack, not when they are popped. This prevents the same node from being pushed multiple times when it is a neighbour of several already-visited nodes.

Similar Articles

  1. Graph - Introduction
  2. Graph - Breadth First Search (BFS)
  3. Graph - Dijkstra's shortest path algorithm
  4. Graph - Kruskal's minimum spanning tree algorithm
  5. Graph - Prim's minimum spanning tree algorithm