Python Program to Implement DFS (Depth-First Search)
def dfs(graph, node, visited): and calls itself for unvisited neighbors.Examples
How to Think About It
Algorithm
Code
def dfs(graph, node, visited=None): if visited is None: visited = [] visited.append(node) for neighbor in graph.get(node, []): if neighbor not in visited: dfs(graph, neighbor, visited) return visited # Example usage graph = {'A': ['B', 'C'], 'B': ['D'], 'C': [], 'D': []} visited_nodes = dfs(graph, 'A') print('Visited order:', visited_nodes)
Dry Run
Let's trace the DFS on graph {'A': ['B', 'C'], 'B': ['D'], 'C': [], 'D': []} starting from 'A'.
Start at node 'A'
visited = ['A']
Visit neighbor 'B' of 'A'
visited = ['A', 'B']
Visit neighbor 'D' of 'B'
visited = ['A', 'B', 'D']
No neighbors for 'D', backtrack to 'B', then 'A'
visited = ['A', 'B', 'D']
Visit neighbor 'C' of 'A'
visited = ['A', 'B', 'D', 'C']
No neighbors for 'C', DFS complete
visited = ['A', 'B', 'D', 'C']
| Step | Current Node | Visited List |
|---|---|---|
| 1 | A | ['A'] |
| 2 | B | ['A', 'B'] |
| 3 | D | ['A', 'B', 'D'] |
| 5 | C | ['A', 'B', 'D', 'C'] |
Why This Works
Step 1: Mark node as visited
When DFS visits a node, it adds it to the visited list to avoid revisiting.
Step 2: Explore neighbors recursively
DFS calls itself on each unvisited neighbor, diving deeper into the graph before backtracking.
Step 3: Backtrack after exploring
Once all neighbors are visited, DFS returns to previous nodes to explore other branches.
Alternative Approaches
def dfs_iterative(graph, start): visited = [] stack = [start] while stack: node = stack.pop() if node not in visited: visited.append(node) stack.extend(reversed(graph.get(node, []))) return visited # Example usage graph = {'A': ['B', 'C'], 'B': ['D'], 'C': [], 'D': []} print('Visited order:', dfs_iterative(graph, 'A'))
def dfs_with_set(graph, node, visited=None): if visited is None: visited = set() visited.add(node) for neighbor in graph.get(node, []): if neighbor not in visited: dfs_with_set(graph, neighbor, visited) return list(visited) # Example usage graph = {'A': ['B', 'C'], 'B': ['D'], 'C': [], 'D': []} print('Visited order:', dfs_with_set(graph, 'A'))
Complexity: O(V + E) time, O(V) space
Time Complexity
DFS visits each vertex and edge once, so the time is proportional to the number of vertices (V) plus edges (E).
Space Complexity
The space used depends on the recursion stack and the visited list, both up to O(V) in the worst case.
Which Approach is Fastest?
Recursive and iterative DFS have similar time complexity; iterative avoids recursion limits but may be slightly more complex to write.
| Approach | Time | Space | Best For |
|---|---|---|---|
| Recursive DFS | O(V + E) | O(V) | Simple code, small to medium graphs |
| Iterative DFS | O(V + E) | O(V) | Large graphs, avoid recursion limits |
| DFS with visited set | O(V + E) | O(V) | Faster membership checks, unordered output |