Go Deep, Then Backtrack
CS205 Data Structures
Use arrow keys or buttons to navigate • Press S to reveal steps
Depth-First Search explores a graph by going as deep as possible along each branch before backtracking.
Imagine exploring a maze: you always walk forward, taking the first available turn. When you hit a dead end, you backtrack to the last intersection and try a different path.
DFS is driven by a stack (either the call stack via recursion, or an explicit stack). It visits vertices in a deep-first manner rather than level-by-level.
The recursive version uses the program's call stack implicitly. The iterative version uses an explicit stack data structure. Both do the same thing.
| Recursive | Iterative | |
|---|---|---|
| Stack | Call stack (implicit) | Explicit Stack object |
| Push | Recursive call | stack.push(v) |
| Pop | Function return | stack.pop() |
| Risk | Stack overflow on deep graphs | Uses heap memory |
Recursive DFS can cause a stack overflow on very deep graphs (e.g., a path of 100,000 nodes). The iterative version avoids this.
The recursion tree of DFS-Visit calls IS the DFS tree of the graph.
The iterative version may visit vertices in a different order than the recursive version, because it pushes ALL neighbors at once. The recursive version visits one neighbor completely before even looking at the next.
Replace the Stack with a Queue, and you get BFS! That is the only structural difference.
Graph with 7 vertices. We start DFS at vertex A. Neighbors processed in alphabetical order.
Press S to reveal each step
Press S to reveal each step
A → B → E → D → G → C → F
Notice how DFS goes deep (A→B→E→D) before backtracking. It does NOT visit level-by-level like BFS would.
When DFS traverses a directed graph, every edge falls into one of four categories:
| Edge Type | Goes To | Meaning |
|---|---|---|
| Tree | Unvisited vertex | Part of DFS tree |
| Back | Ancestor in DFS tree | Indicates a cycle! |
| Forward | Descendant in DFS tree | Shortcut down |
| Cross | Neither ancestor nor descendant | Between branches |
In undirected graphs, there are only tree edges and back edges. Forward and cross edges cannot exist.
Back edge = cycle. This is the foundation of DFS-based cycle detection.
DFS assigns two timestamps to each vertex: d[v] (discovery) and f[v] (finish).
For any two vertices u and v, exactly one of these is true:
[d[u], f[u]] and [d[v], f[v]] are entirely disjoint (neither is ancestor of other)They never partially overlap. Like properly nested parentheses!
Think of each vertex as a box that opens at time d[v] and closes at f[v]. Boxes are either completely inside one another or completely separate -- they never partially overlap.
| Property | BFS | DFS |
|---|---|---|
| Data structure | Queue | Stack |
| Exploration | Level by level | Deep first |
| Shortest path? | Yes (unweighted) | No |
| Path existence? | Yes | Yes |
| Cycle detection | Possible | Natural |
| Topological sort | No | Yes |
| Time complexity | O(V + E) | O(V + E) |
| Space complexity | O(V) | O(V) |
Use vertex colors (states):
| Color | State | Meaning |
|---|---|---|
| WHITE | Undiscovered | Not yet visited |
| GRAY | Discovered | In progress (on stack) |
| BLACK | Finished | Fully explored |
Edge to a GRAY vertex = Back edge = CYCLE! A gray vertex is still being processed (it's an ancestor on the current DFS path).
In undirected graphs, every edge appears twice (u-v and v-u). We must exclude the parent edge when checking for back edges.
A linear ordering of vertices such that for every directed edge u → v, vertex u comes before v.
Topological sort = reverse of DFS finish order. When a vertex finishes (all descendants explored), prepend it to the result.
Topological sort only works on DAGs (Directed Acyclic Graphs). If there is a cycle, no valid topological order exists.
Underwear before pants, socks before shoes, shirt before jacket. Topological sort gives you an order that respects ALL dependencies.
Finish order: CS401, CS301, CS302, CS202, CS201, MATH1, CS102, CS101
Reversed (topological):
CS101, CS102, MATH1, CS201, CS202, CS302, CS301, CS401
Every course appears after all its prerequisites. Valid schedule!
Press S to reveal steps
Each call to DFS from the outer loop discovers exactly one connected component. The total number of outer-loop calls = number of components.
Press S to reveal steps
An SCC is a maximal set of vertices where every vertex is reachable from every other vertex.
Think of SCCs as "islands" in a directed graph where you can travel between any two cities on the same island. Kosaraju's finds these islands by looking at the graph forwards AND backwards.
DFS creates long, winding corridors with few branches -- a "perfect maze" (exactly one path between any two cells).
DFS naturally backtracks when it hits dead ends, making it perfect for exploring mazes. It finds a path (not necessarily the shortest). Use BFS if you need the shortest path.
DFS maze solving is like the "always follow the left wall" strategy. You explore one path completely before trying another.
| Representation | DFS Time | DFS Space |
|---|---|---|
| Adjacency List | O(V + E) | O(V) |
| Adjacency Matrix | O(V2) | O(V) |
With an adjacency matrix, finding neighbors of a vertex takes O(V) instead of O(degree(v)), so total time becomes O(V2) regardless of the number of edges.
| Property | Value |
|---|---|
| Data Structure | Stack (or recursion) |
| Strategy | Go deep, then backtrack |
| Time | O(V + E) |
| Space | O(V) |
| Shortest Path? | No (use BFS) |
| Complete? | Yes (finite graphs) |