Depth First Search (DFS)
Start here BFS spreads out carefully, layer by layer. Depth first search is the adventurous explorer: it rushes down one path as far as it can, and only when it hits a dead end does it backtrack to the last place where it had another choice. It's like exploring a maze by always taking the next unexplored turn.
The idea
- Start from vertex i. Pick an unexplored neighbour j.
- Suspend (pause) the exploration of i and explore j instead.
- Keep going deeper until you reach a vertex with no unexplored neighbours.
- Backtrack to the most recently suspended vertex that still has an unexplored neighbour, and continue from there.
Suspended vertices are kept on a stack — last in, first out. The most recently suspended vertex is checked first.
| BFS | DFS | |
|---|---|---|
| Order | Level by level | One path as deep as possible, then backtrack |
| Waiting vertices kept in | Queue (first in, first out) | Stack (last in, first out) |
| Paths found | Shortest (fewest edges) | Not necessarily shortest |
Everyday example: Exploring a maze. At every junction you take the first passage you haven't tried. At a dead end, you walk back to the last junction with an untried passage. The trail of junctions you walked back through is your "stack".

Full trace — DFS from vertex 4 (undirected airline graph)
Adjacency list (neighbours tried in this order): 0:[1, 2, 4], 1:[0, 2], 2:[0, 1], 3:[4, 5, 6], 4:[0, 3, 7], 5:[3, 6, 7, 8], 6:[3, 5], 7:[4, 5, 8], 8:[5, 7, 9], 9:[8].
| Step | What happens | Stack of suspended vertices |
|---|---|---|
| 1 | Mark 4. Suspend 4, explore its first unvisited neighbour 0 | 4 |
| 2 | Suspend 0, explore 1 | 4, 0 |
| 3 | Suspend 1, explore 2 | 4, 0, 1 |
| 4 | 2 has no unvisited neighbours (0, 1 done). Backtrack to 1, then 0, then 4 — none of 1, 0 have unvisited neighbours left | 4 |
| 5 | 4 still has unvisited neighbour 3. Suspend 4, explore 3 | 4 |
| 6 | Suspend 3, explore 5 | 4, 3 |
| 7 | Suspend 5, explore 6 | 4, 3, 5 |
| 8 | 6 is a dead end. Backtrack to 5; 5 still has unvisited 7. Explore 7 | 4, 3, 5 |
| 9 | Suspend 7, explore 8 | 4, 3, 5, 7 |
| 10 | Suspend 8, explore 9 | 4, 3, 5, 7, 8 |
| 11 | 9 is a dead end. Backtrack to 8, 7, 5, 3, 4 — nothing left. Done. | (empty) |
Order in which DFS visits vertices: 4, 0, 1, 2, 3, 5, 6, 7, 8, 9.

Notice: DFS reached 2 by the path 4 – 0 – 1 – 2 (3 edges), but the shortest path is 4 – 0 – 2 (2 edges). DFS paths are not necessarily shortest.
Implementing DFS — recursion does the stack for us
DFS is most natural as a recursive function: for each unvisited neighbour k of v, call DFS(k). The recursion itself keeps the stack of suspended calls — we don't need to manage a stack ourselves.
(visited, parent) = ({}, {}) # global dictionaries
def DFSInitListGlobal(AList):
# Initialization
for i in AList.keys():
visited[i] = False
parent[i] = -1
return
def DFSListGlobal(AList, v):
visited[v] = True
for k in AList[v]:
if (not visited[k]):
parent[k] = v
DFSListGlobal(AList, k)
returnUsage:
DFSInitListGlobal(AList)
DFSListGlobal(AList, 4)visitedandparentare global, so every recursive call updates the same dictionaries.- We still need a separate initialization step to set them up for the size of the graph.
Result for DFS from 4: parent = {0: 4, 1: 0, 2: 1, 3: 4, 4: −1, 5: 3, 6: 5, 7: 5, 8: 7, 9: 8}.
How long does DFS take?
Like BFS, each vertex is marked once and explored once. Exploring v means scanning all its neighbours.
| Representation | Time to explore v | Total |
|---|---|---|
| Adjacency matrix | n (whole row) | O(n²) |
| Adjacency list | degree(v) | O(n) setup + O(m) for all edges = O(n + m) |
Same as BFS.
Why DFS is still useful
DFS doesn't give shortest paths, but the order in which it visits and finishes vertices reveals a lot about the graph's structure — cycles, connected pieces, and more. That's the next page.
In one line: DFS goes as deep as possible and backtracks using a stack (or recursion), takes O(n + m) with adjacency lists, but its paths are not necessarily shortest.
Exam words: DFS, suspend, backtrack, stack (LIFO), recursion keeps the stack, global visited/parent, O(n²) matrix, O(n + m) list, not shortest paths.
Quick check
1. Which data structure does DFS use (explicitly or through recursion)?
A stack — last in, first out.2. Run DFS from 9 on the undirected airline graph (neighbours in increasing order). In what order are vertices visited?
9 → 8 → 5 → 3 → 4 → 0 → 1 → 2, backtrack to 4 → 7, backtrack to 3 → 6. Order: 9, 8, 5, 3, 4, 0, 1, 2, 7, 6.3. Does DFS always find the shortest path? Give an example from the trace above.
No. DFS from 4 reaches 2 via 4 – 0 – 1 – 2 (3 edges), but 4 – 0 – 2 has only 2 edges.4. Why don't we need an explicit stack in the recursive DFS?
Each recursive call waits (is suspended) until its inner call finishes — Python's call stack keeps track of the suspended vertices for us.5. What is the complexity of DFS with an adjacency list?
O(n + m).Try Interactive Playground
See how breadth-first search explores a graph one layer at a time.
Start in Manual mode. Read each card, make your prediction, then choose Show next step when you are ready.
Put A on the stack.
Why? The stack records where DFS can continue after exploring a path.
- enqueue(start); mark visited
- current = dequeue()
- visit current
- enqueue each unvisited neighbour
- skip an already visited neighbour
- stop when queue is empty
Before you continue
Look at the highlighted item. What should the algorithm do next?
Complexity
Time: O(V + E); space: O(V). BFS finds shortest paths by number of edges in unweighted graphs.
