StudyHub

Algorithms · Graphs

Depth-first search (DFS)

Start at vertex 0 and always go as deep as you can: 0 → 1 → 3 → 2. At 2 every neighbour is already visited, so backtrack to 3 and try its other neighbour, 4. That's depth-first search: dive down one path, back up only when stuck.

Why it matters: DFS visits every reachable vertex in $O(V + E)$ with just a stack (or recursion), and the order in which it enters and leaves vertices is what detects cycles, orders tasks topologically and finds connected components.

Prerequisites

The analogy

Everyday analogy

Explore a maze with one rule: keep a hand on the wall and walk until you hit a dead end, then go back to the last junction and take the next corridor.

DFS explores a graph exactly like that. The recursion (or an explicit stack) remembers the junctions, so backtracking always returns to the most recent unfinished one.

How it works

Precondition: none. Any graph, directed or undirected, weighted or not. DFS does not find shortest paths.

Start with one DFS by hand

Same graph as BFS: 0–1, 0–2, 1–3, 2–3, 3–4. Start at 0 and always try neighbours in increasing order. Predict, then open "Check".

Example: predict before you read

Step 1. From 0, which vertex do you visit next, and then from there?

Check

0 → 1 (smallest unvisited neighbour of 0), then 1 → 3 (1's neighbours are 0, visited, and 3).

Step 2. Cover the next line and guess: from 3, where do you go?

Check

3's neighbours are 1 (visited), 2 and 4. Take 2. From 2, both neighbours 0 and 3 are visited: dead end.

Step 3. Backtrack. Predict the final visit order.

Check

Back to 3, which still has 4 unvisited: visit 4. Then everything is finished. Order: 0, 1, 3, 2, 4.

DFS from vertex 0 with visit order numbers; tree edges in green and the non-tree edge 0-2 dashed
What you just did by hand: green edges form the DFS tree; the dashed edge 0–2 was never used to discover anything. Small numbers are the visit order.

Quick recall: Before reading further, cover the text above and say out loud: when does DFS backtrack?

Check your answer

When the current vertex has no unvisited neighbour left; it returns to the vertex it came from.

Finish the picture: discovery and finish times

Number each event: entering a vertex, and leaving it after all its neighbours are done.

  • Order of entering. Predict it.
    Check

    0, 1, 3, 2, 4 (the visit order above).

  • Order of finishing. Predict it.
    Check

    2 finishes first (dead end), then 4, then 3, then 1, then 0. Finish order is what topological sort uses.

Quick recall: Without scrolling up, say out loud: why do DFS and BFS give different orders on the same graph?

Check your answer

BFS finishes a whole layer before going deeper (queue); DFS follows one path to the end first (stack).

The rule in plain words

Visit a vertex: mark it, then for each unvisited neighbour, visit it recursively. When no unvisited neighbour remains, return (backtrack).

Run it from every still-unvisited vertex to cover a disconnected graph; each run explores one connected component.

Quick recall: Before you read the code, write the recursive DFS in your own words. Then check below.

Check your answer

Mark the vertex visited and record it. For each neighbour, if it is unvisited, run DFS from it. When the loop ends, return.

The code

Read this code after you have done the DFS by hand. It will make more sense.

int dfs(int adj[][5], int n, int u, int visited[], int order[], int k) {
    visited[u] = 1;
    order[k++] = u;                                   /* record when first reached */
    for (int v = 0; v < n; v++)
        if (adj[u][v] && !visited[v])
            k = dfs(adj, n, v, visited, order, k);    /* go deep first */
    return k;                                         /* all neighbours done: backtrack */
}

Definition

What you just did, in one line: depth-first search explores a graph by following one path as deep as possible and backtracking at dead ends, using recursion or a stack.

flowchart TD
  A["visit u: mark it"] --> B{"u has an unvisited neighbour v?"}
  B -->|yes| C["visit v (go deeper)"]
  C --> B
  B -->|no| D["backtrack: return to the caller"]
          
Before you scroll past this diagram, cover the right side and guess: at vertex 2 in our example, which arrow do you take? (Answer: "no", straight to backtrack, since 0 and 3 are visited.)

How fast is it?

Quick recall: Before looking at the formula, predict: does DFS cost more or less than BFS?

Check your answer

The same, $O(V + E)$: each vertex is visited once and each edge examined a constant number of times.

Each vertex is visited once, and its adjacency list is scanned once during that visit:

$$O\Big(V + \sum_{v} \deg(v)\Big) \;=\; O(V + E)$$

The recursion depth can reach $V$ (on a long path), so the stack needs $O(V)$ space.

MeasureCostWhy
Time (adjacency lists)$O(V + E)$Each vertex and edge handled once
Time (adjacency matrix)$O(V^2)$Each visit scans a full row
Extra space$O(V)$Visited flags plus recursion depth
Shortest pathsNoIt may reach a vertex by a long detour first

Warning

Recursive DFS on a graph with a very long path (say $10^6$ vertices in a line) can overflow the call stack. Use an explicit stack in that case.

Exam tip

Know which problems DFS solves: cycle detection, topological sort, connected components, and path existence. Shortest unweighted path is BFS's job, not DFS's.

Common mistakes

  • Marking a vertex after exploring its neighbours, so cycles cause infinite recursion.
  • Expecting DFS to find shortest paths.
  • Running it only from vertex 0 on a disconnected graph and missing other components.

Try it yourself

Exercise 1 (warm-up)

Run DFS from vertex 3 on the same graph (neighbours in increasing order). Give the visit order.

Hint

3's smallest neighbour is 1.

Answer

3, 1, 0, 2, 4. From 1 go to 0, from 0 to 2, then 2 is stuck, so back up to 3, which still has 4.

Exercise 2 (a bit harder)

A graph has edges 0–1, 2–3, 3–4 and an isolated vertex 5. How many DFS runs from unvisited vertices are needed to visit everything, and what does that number mean? Write your answer down before you open it.

Show answer

3 runs: from 0 (visits 0, 1), from 2 (visits 2, 3, 4) and from 5. The number of runs is the number of connected components.

Did you know?

Did you know?

DFS goes back to the 19th century: Charles Pierre Trémaux described it as a method for escaping mazes using chalk marks on the walls.

Your computer uses DFS ideas to find which objects are still reachable during garbage collection, and in tools like find that walk a directory tree.

Key points to remember

If you remember one thing from each line, remember the because.

  • DFS dives deep first because it recurses into a neighbour before looking at the next one.
  • It backtracks to the latest junction because the call stack is last-in, first-out.
  • It is $O(V + E)$ because each vertex is visited once and each adjacency list scanned once.
  • It does not give shortest paths because it may reach a vertex through a long detour first.
  • Finish order matters because a vertex finishes only after everything reachable from it.

Self-check questions

Answer these in order, out loud. They follow the page, so your answers become the script of your video.

  1. How is DFS like exploring a maze with a hand on the wall?
    Sample answer

    You follow one corridor to its end, then go back to the last junction and try the next corridor.

  2. Walk through DFS from 0 on the graph 0–1, 0–2, 1–3, 2–3, 3–4.
    Sample answer

    0 → 1 → 3 → 2; 2 is a dead end, so back to 3 → 4; then everything finishes. Order 0, 1, 3, 2, 4.

  3. Why does the code mark visited[u] before looping over neighbours?

    A good answer mentions cycles.

    Sample answer

    If u weren't marked first, a cycle could lead back to u and start visiting it again forever.

  4. So what is DFS, in one sentence?
    Sample answer

    A traversal that follows one path as deep as possible, backtracking at dead ends, using recursion or a stack.

  5. Why can't DFS be used for shortest paths?

    A good answer uses vertex 2 in the example.

    Sample answer

    DFS reached 2 via 0 → 1 → 3 → 2, three edges, although 2 is a direct neighbour of 0. The first arrival is not necessarily the shortest.

  6. What are DFS's time and space costs?
    Sample answer

    $O(V + E)$ time on adjacency lists and $O(V)$ space for visited flags and the recursion stack.

  7. What problems is DFS good for?
    Sample answer

    Cycle detection, topological sorting, finding connected components, and checking whether a path exists.

  8. If you had to teach this to a friend in 2 minutes, what would you leave out, and why?
    Sample answer

    I'd leave out finish times and the stack-overflow issue. I'd keep the maze analogy, the 0, 1, 3, 2, 4 trace with the backtrack at 2, and "deep first, $O(V + E)$, not for shortest paths".

External resources

Videos

Review schedule

Come back after 3, 7, 14, then 30 days. Spacing beats cramming.

Feynman prompt

Explain this topic in your own words, as if teaching a beginner.

Explain it in your own words first, then record a 2–3 minute video on your phone.

25:00

Ready to record? Open your phone camera now and record a 2–3 minute explanation.

My notes

Saved only in this browser (localStorage). Nothing is uploaded.