Depth-First Search (DFS) in Python
Learn Depth-First Search (DFS) in Python! A super fun guide to exploring graphs, escaping mazes, and backtracking with recursive and iterative code.
Try it yourself
Run this code directly in your browser. Click "Open in full editor" to experiment further.
Click Run to see output
Or press Ctrl + Enter
How it works
Ready to go deep? Depth-First Search (DFS) is one of the most beloved algorithms in all of computer science, and once it clicks, graphs stop feeling scary and start feeling like playgrounds. ๐คฟ
DFS is a graph traversal strategy where you pick a start node, charge down one branch as far as you can, and only when you hit a dead end do you backtrack and try the next path. It's the algorithmic version of "go deep first, ask questions later" โ exactly right for solving mazes, scheduling tasks, parsing nested structures, hunting cycles, and crawling deeply linked sites. Whenever the path itself matters more than how many nodes you touch, DFS is the tool you reach for.
Big idea: BFS asks "what's near me?" โ DFS asks "how far can I go down this road?"
How DFS Works
Let's walk through it on a small graph:
A
/ \
B C
/ \ \
D E - FStarting at A, DFS picks one neighbour (say B), goes to B, then dives further to D. D has no unvisited neighbours, so we backtrack to B and try its next child, E. From E we slide over to F. F has no new neighbours, so we backtrack all the way up to A, and finally visit C (whose only neighbour F is already visited). Final order: A โ B โ D โ E โ F โ C.
The pattern is always the same:
1. Mark the current node as visited.
2. For each unvisited neighbour, recurse (or push onto a stack).
3. When you run out of neighbours, return โ that's the backtrack.
The visited set is non-negotiable on a real graph. Without it, any cycle becomes an infinite loop. ๐
Recursive vs Iterative DFS
Two flavours of DFS in Python, same algorithm wearing different clothes:
sys.getrecursionlimit()), so a chain of 1500 nodes blows up with RecursionError. You can bump it with sys.setrecursionlimit(...), but the C stack is finite โ don't go wild.list as a stack โ append to push, pop to pop. No recursion limit, no overflow, identical results if you order neighbours carefully.Pick recursion for trees, small graphs, and readable code. Pick iteration for production, very deep graphs, or anything user-facing where RecursionError would be embarrassing.
Pre-order, In-order, Post-order in DFS
These terms come from binary trees but they generalise to graphs as when you record the node:
On graphs, pre-order tells you when nodes were discovered; post-order tells you when they were finished. That finish-order is what makes DFS so powerful for dependency resolution.
Complexity Analysis
| Resource | Cost | Why |
|---|---|---|
| Time | O(V + E) | Every vertex marked once, every edge inspected once |
| Space (visited set) | O(V) | One entry per node |
| Space (recursion / explicit stack) | O(V) worst case | A long thin path forces every node onto the stack |
So DFS is linear in the size of the graph โ you can't do meaningful traversal faster than that.
DFS vs BFS
| DFS | BFS | |
|---|---|---|
| Strategy | Go deep, then backtrack | Visit all neighbours first |
| Data structure | Stack (or recursion) | Queue (collections.deque) |
| Shortest path on unweighted graph? | โ No | โ Yes |
| Memory profile | O(text{depth}) โ friendly on wide graphs | O(text{width}) โ can balloon on bushy graphs |
| Best for | Cycles, topo sort, backtracking, components | Shortest path, level-order, peer discovery |
If you need the shortest number of hops, use BFS. If you need to enumerate paths, detect cycles, or order dependencies โ that's recursive DFS (or iterative) territory.
Common Pitfalls
reversed()s neighbours so the leftmost ends up on top of the stack โ match this if you want both versions to agree.Real-World Uses
DFS Variants
Frequently Asked Questions
Can DFS find the shortest path? Not on a general graph โ it finds a path. Use BFS for unweighted, Dijkstra or A* for weighted.
How do I avoid stack overflow? Use the iterative version with an explicit list-as-stack, or call sys.setrecursionlimit(10_000) for moderate increases.
Iterative vs recursive โ which is faster? Same Big-O. Iterative is often slightly faster in Python (no function-call overhead), but the gap is small. Choose for clarity, not micro-speed.
Why is DFS used for topological sort? The post-order finish time of DFS on a DAG, reversed, is a valid topological ordering. No other traversal gives you that for free.
Can DFS detect cycles? Yes โ track nodes in three states (unvisited, in-progress, done). Hitting an in-progress node means you found a back-edge โ a cycle.
Difference between DFS and backtracking? Backtracking is DFS over a search tree of decisions, plus pruning. Every backtracking algorithm โ Sudoku, N-queens, CSPs โ is a DFS in disguise.
Have fun tracing the paths your depth-first search takes โ once DFS clicks, you'll spot it everywhere. ๐
Related examples
Breadth-First Search (BFS) in Python
Learn Breadth-First Search (BFS) in Python! A beginner-friendly, fun guide to exploring graphs level by level.
Binary Tree in Python
Learn Binary Trees in Python! A fun, beginner-friendly guide to building trees, inserting nodes, and exploring inorder, preorder, and postorder traversals.