ma'am audrey's lessons

ITC 120 › Final › Graphs

Graphs · Lesson 3 of 3

Depth-First Search (DFS)

This lesson teaches:Algorithm

Go as far as you can down one path. At a dead end, back up and try the next way.

By the end, you can:

  1. run DFS by hand with a stack (neighbors in alphabetical order), showing each push and each backtrack (pop), and write the DFS order;
  2. explain why a recursive DFS needs no stack of its own (each call is a push, each return is a pop);
  3. use DFS to list what a vertex can reach, and read a path to a vertex off the stack;
  4. say why that path may not be the shortest, and choose BFS or DFS for a task;
  5. match each line of a Java DFS to the steps of the procedure.

You need: the BFS lesson, the Stacks lesson, the recursion lesson, and scratch paper.

Part 1 · The idea

Deep first, then back up

Depth-first search (DFS)

Depth-first search (DFS) is a graph traversal algorithm that visits every vertex reachable from a starting vertex. It starts at one vertex and keeps moving to an unvisited neighbor, going as deep as possible. When it reaches a vertex with no unvisited neighbors (a dead end), it backtracks: it goes back to the previous vertex and tries another neighbor. It uses a stack.

DFS from A: go deep, then back upABCDEFGstart and enddead end!back up to Edead end!Then nothing new is left: back up E → F → C → B → A.go deeperback upVisit order:A1B2C3F4E5D6G7
DFS from A on the main example ( ). It goes A → B → C → F → E → D. D has no unvisited neighbors (a dead end), so DFS backs up to E and goes deeper to G. G is a dead end too, so it backs up to E again. Now E, F, C, B, and A have no unvisited neighbors left, so DFS backs up through each of them to A, and the stack is empty.

Think of opening folders

You are looking for a file on a computer. You open the first folder, then the first folder inside it, then the first folder inside that... until a folder has no more folders inside. Then you click Back once and open the next folder there. The Back button is the stack: it remembers the way you came.

DocumentsITC120PhotosWeek 9Week 1012345(empty)(empty)(empty)Inside Week 9:Documents ›ITC120 ›Week 9open (go deeper)Back (go back up)Back remembers the way you came, newest first:that is a stack.
Open folders deep first: Documents, ITC120, Week 9. Week 9 is empty, so click Back: you return to ITC120, the folder you came from, and open Week 10. Two more Backs bring you to Documents, which still has Photos. Photos is empty too, so one last Back, and you have seen every folder.

Compare with BFS: BFS spreads out in waves (wide first). DFS dives down one path (deep first). Same graph, same start, different order:

BFS from AA, B, D, C, E, F, G
DFS from AA, B, C, F, E, D, G

Part 2 · The tool

Why a stack?

Recall the stack: you push onto the top and pop from the top. Last in, first out, like a stack of plates.

In DFS, the stack holds the path you are currently on. The top is where you are now. Pushing = taking one step deeper. Popping = taking one step back. Because the most recent vertex comes off first, you always back up to the place you just came from.

The stack is the path you are onABCDEFGyou are herenot visited yetBeforeABCFEDtop = D, where you aredead end, so pop DAfter the popABCFEnew top = E, thevertex you came frompop DBottom to top, each stack is the path from A.
One moment of DFS from A: it has just reached D. Before: the stack, bottom to top, is A, B, C, F, E, D, exactly the path it walked. D is a dead end, so pop it. After the pop: the new top is E, the vertex DFS came from, so that is where it goes back to.

The DFS procedure

Every DFS follows these steps:

  1. Visit the start vertex and push it onto the stack.
  2. While the stack is not empty, look at the vertex on top:
    1. If it has an unvisited neighbor, take the first one in alphabetical order (or smallest number first, if the vertices are numbers). Visit it and push it. (Go deeper.)
    2. If all its neighbors are visited, pop it. (Backtrack.)
  3. When the stack is empty, DFS is done.

Exam traps

  1. Popping is not the end. After you pop, look at the new top: it may still have an unvisited neighbor. In , after popping D the new top is E, and E still has G. So push G. DFS stops only when the stack is empty.
  2. Write a vertex when you push it, not when you pop it. Pushing is the first visit. If you write the vertices as they are popped, you get a different list. Main example from A:

    ✗ Pop order (not the DFS order)D, G, E, F, C, B, A

    ✓ DFS order (push order)A, B, C, F, E, D, G

  3. The neighbor order changes the answer. At A, both B and D are unvisited. Taking B first (alphabetical) gives one order; taking the last neighbor first (Z to A) gives another. Both are correct DFS orders, because DFS itself does not say which neighbor comes first:

    ✓ AlphabeticalA, B, C, F, E, D, G

    ✓ Z to A (also a correct DFS)A, D, E, G, F, C, B

    Many books, courses, and exams still add a rule, usually alphabetical order, so everyone gets the same answer and answers are easy to check. If the question gives a rule, follow it. If it gives none, any correct order is fine, but alphabetical is the safe habit: it matches most answer keys. For numbered vertices, the usual rule is smallest number first, comparing them as numbers (2 before 10).
Extra: the "push-all" way in some books

Extra topic. Open this if a book or website shows DFS with a different stack routine. In this course, use the procedure above.

Some books and websites use a different stack routine: pop a vertex; if it is not visited yet, visit it and push all its unvisited neighbors. Its stack is a to-do list, not the path you are on:

Just visited D: what is on each stack?Our waythis courseABCFEDPush-all waysome booksDvisited: skipEvisited: skipGto doGagain!the path A → … → Da to-do list, not a pathBoth end with the same visit order here: A B C F E D G.
Right after DFS from A visits D. Our stack is the path from A to D. The push-all stack holds D and E, which are already visited (they get skipped when popped), and G twice.

Does the push-all way visit the vertices in the same order as ours? It depends on which neighbor it pushes last, because a stack gives back the last thing pushed first. Look at the start, A, whose unvisited neighbors are B and D:

  • Push D, then B (Z to A): B is on top, so B is visited next, just like ours. The whole order comes out the same: A, B, C, F, E, D, G.
  • Push B, then D (A to Z): D is on top, so D is visited next. The order changes: A, D, E, G, F, C, B.

One warning: some versions tick a vertex as visited when it is pushed, instead of when it is popped. That shortcut can give an order that is not a real DFS. In this course, use the procedure above.

Part 3 · Watch it

DFS, step by step

Tap Next step and watch the stack grow and shrink. Before each tap, guess: go deeper or backtrack?

DFS lab

Orange = where you are now (top of the stack). Light teal = on the stack. Dark teal = visited and finished. Thick teal edges = the steps DFS took.

Start at:

Stack (last in, first out)

Visited order

    Color key
    current on the stack visited edges used

    The three buttons are three different graphs, not three kinds of DFS. Directed starts at F: follow the arrows and see how few vertices DFS can reach. Separate pieces starts at A: DFS stops after A, B, C, because nothing connects them to the rest.

    DFS and recursion

    "Go deeper, and when stuck, go back to where you came from" is exactly how recursion works. Each recursive call is a push; each return is a pop. That is why DFS is usually written as a short recursive method (Part 7), and Java's call stack does the stack work for you.

    Part 4 · What DFS is good for

    Reaching and path-finding

    1. What can be reached?

    Like BFS, DFS visits everything reachable from the start, and nothing else. In a directed graph, this answers "where can I get to from here?"

    ABCDEF
    StartDFS orderNever visited
    AA, B, C, D, EF
    FF, D, EA, B, C

    From F you can only go to D, then E. The arrows never lead back to A, B, or C. From A, everything except F is reachable, because no arrow points into F.

    2. Finding a path

    The stack is the path you are on. So the moment DFS reaches a target, the stack (bottom to top) is a path from the start to the target.

    DFS path A → G

    ABCDEFG

    A → B → C → F → E → G

    5 edges

    BFS path A → G

    ABCDEFG

    A → B → E → G

    3 edges (shortest)

    DFS found a path, but a long one. It wandered deep before reaching G. If you need the fewest edges, use BFS.

    Where you see DFS in IT

    Searching all folders on a drive, solving mazes and puzzles, and checking for cycles. For example, a project tool can check if tasks wait on each other in a circle (task A waits for B, B waits for C, C waits for A), which would mean nothing can ever start.

    Solving a mazeGoal!← fork1234Start1Try one way (up).2Dead end!3Back up to the fork.4Try the next way.Finding a cycle in a project planTask ADesignTask BBuildTask CTestwaits forwaits forwaits forstuck!no task can startDFS finds it: it reaches a task that is still on its stack.
    A maze is a graph: each fork or corner is a vertex, and each hallway is an edge. Like DFS, the cat follows one way to its end (1). At a dead end (2), it backs up to the last fork (3) and tries the next way (4). In a project plan, if DFS reaches a task that is still on its stack, it has gone around a circle: those tasks wait for each other forever. (Searching folders works like .)

    Part 5 · Side by side

    BFS vs. DFS

    Here is the same graph searched both ways from A. The small numbers show the visit order, and the arrows show how each vertex was first reached.

    BFS: wide firstuses a queueA1B2C4D3E5F6G7Order: A B D C E F G (level by level)DFS: deep firstuses a stackA1B2C3D6E5F4G7Order: A B C F E D G (one path, then back up)
    BFS reaches D third, straight from A, because D is one edge away. DFS reaches D sixth, at the end of a long path. Both visit all seven vertices, just in a different order.

    Both are graph traversal algorithms: they visit every reachable vertex once. They differ in these ways:

    BFSDFS
    Exploreswide first, level by leveldeep first, then backtracks
    Uses aqueue (first in, first out)stack (last in, first out), or recursion
    Shortest path (fewest edges)?yes, in unweighted graphsno, not guaranteed
    Finds what is reachable?yesyes
    Good forfewest hops, levels, "friends of friends"exploring every path, mazes, folders, finding cycles

    How to remember

    BFS = Broad (queue, waves). DFS = Deep (stack, backtrack).

    Part 6 · Your turn

    Try it on paper

    How to set up your paper

    • Draw the stack as a column. Write new vertices on top; cross out the top when you pop.
    • Write the DFS order as you push each vertex.
    • On the drawing, trace each step with a pencil so you can see the dead ends.

    Problem 1

    This is the same graph as Problem 1 in the BFS lesson.

    ABCDEFG
    1. Do a DFS starting at D (neighbors in alphabetical order). Write the DFS order.
    2. Compare it with your BFS order from D.
    Show the answer

    DFS order: D, B, A, C, E, F, G

    BFS order (from the BFS lesson): D, B, C, F, G, A, E

    The trace, one row per push or pop. Each row shows the same stack, one step later (bottom → top, left to right):

    StepActionStack (bottom → top)DFS order so far
    1push DDD
    2push BD, BD, B
    3push AD, B, AD, B, A
    4push CD, B, A, CD, B, A, C
    5push ED, B, A, C, ED, B, A, C, E
    6push FD, B, A, C, E, FD, B, A, C, E, F
    7pop FD, B, A, C, ED, B, A, C, E, F
    8pop ED, B, A, CD, B, A, C, E, F
    9pop CD, B, AD, B, A, C, E, F
    10pop AD, BD, B, A, C, E, F
    11pop BDD, B, A, C, E, F
    12push GD, GD, B, A, C, E, F, G
    13pop GDD, B, A, C, E, F, G
    14pop D(empty)D, B, A, C, E, F, G

    DFS went D → B → A → C → E → F before backing up. F was a dead end (D and E visited), so it popped back through E, C, A, and B to D, which still had G unvisited.

    Problem 2 (directed)

    ABCDEF
    1. Do a DFS starting at C, following the arrows. Write the DFS order.
    2. Which vertices are not visited?
    Show the answer

    DFS order: C, B, D, E

    Not visited: A and F. From C the arrows go C → B → D → E, and E's only arrow leads back to C (already visited). No arrow from this group points to A or F.

    Part 7 · In code

    DFS in Java

    DFS is short when written with recursion. This program builds the main example and runs DFS from A.

    public class DFS {
      static char[] names = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
      static int n = names.length;
      static int[][] adj = new int[n][n];
      static boolean[] visited = new boolean[n];
    
      static void addEdge(int a, int b) {   // undirected
        adj[a][b] = 1;
        adj[b][a] = 1;
      }
    
      static void dfs(int u) {
        visited[u] = true;
        System.out.print(names[u] + " ");   // visit
    
        for (int v = 0; v < n; v++) {       // A to Z order
          if (adj[u][v] == 1 && !visited[v]) {
            dfs(v);                         // go deeper
          }
        }
        // loop ended: no unvisited neighbors left,
        // so this call returns (= backtrack / pop)
      }
    
      public static void main(String[] args) {
        addEdge(0, 1); addEdge(0, 3);   // A-B, A-D
        addEdge(1, 2); addEdge(1, 4);   // B-C, B-E
        addEdge(2, 5); addEdge(3, 4);   // C-F, D-E
        addEdge(4, 5); addEdge(4, 6);   // E-F, E-G
        addEdge(5, 6);                  // F-G
        dfs(0);   // prints: A B C F E D G
      }
    }

    ▶ Try it in the Programiz online compiler: tap Copy, paste it there, change public class DFS to public class Main (Programiz saves your code as Main.java, and Java needs the file name and the public class name to match), then press Run.

    Most lines match the BFS program (see its read-aloud table). Three lines to read aloud:

    CodeRead it as
    static boolean[] visited = new boolean[n];"make n true/false slots named visited, all false at first." It is static (outside the method) so every recursive call of dfs shares the same array.
    adj[u][v] == 1 && !visited[v]"v is a neighbor of u and v is not visited yet."
    dfs(v);"go deeper: run dfs on v all the way, then come back and continue this loop."

    Match the code to the procedure

    Calling dfs(v) is the push (go deeper). When the for loop ends, the method returns: that is the pop (backtrack). The visited array stops us from going around a cycle forever. Compare with BFS: there is no queue here, because Java's call stack is the stack.

    Your coding task: count what DFS can reach

    In , DFS from A reached 5 vertices, but DFS from F reached only 3. Now write a DFS that counts them.

    Finish countReach in about 8 lines where it says YOUR CODE. It is the dfs method from this part with a count added. Each call counts its own vertex (1), plus everything its deeper calls reach. Like height in the Recursion lesson, a recursive call gives back a number, and you add it. The graph is the one-way graph, built with addArrow (one direction only). main clears visited before each new test. Run it: each printed line must match its expected comment.

     1public class Main {
     2  static char[] names = {'A', 'B', 'C', 'D', 'E', 'F'};
     3  static int n = names.length;
     4  static int[][] adj = new int[n][n];
     5  static boolean[] visited = new boolean[n];
     6
     7  static void addArrow(int from, int to) {   // directed
     8    adj[from][to] = 1;
     9  }
    10
    11  // DFS from u. Returns how many vertices it reaches,
    12  // counting u itself.
    13  static int countReach(int u) {
    14    // YOUR CODE:
    15    // 1. Mark u as visited.
    16    // 2. Start a count at 1 (for u itself).
    17    // 3. Check every v from 0 to n - 1. If v is a neighbor
    18    //    of u and v is not visited yet, go deeper with
    19    //    countReach(v), and add what it returns to the count.
    20    // 4. Return the count.
    21
    22    return 0;
    23  }
    24
    25  public static void main(String[] args) {
    26    // the one-way graph from Part 4
    27    addArrow(0, 1); addArrow(1, 2);   // A->B, B->C
    28    addArrow(2, 0); addArrow(0, 3);   // C->A, A->D
    29    addArrow(3, 4); addArrow(5, 3);   // D->E, F->D
    30
    31    System.out.println(countReach(0));   // expected: 5 (A B C D E)
    32
    33    visited = new boolean[n];            // clear for the next test
    34    System.out.println(countReach(5));   // expected: 3 (F D E)
    35
    36    visited = new boolean[n];
    37    System.out.println(countReach(4));   // expected: 1 (only E)
    38
    39    visited = new boolean[n];
    40    System.out.println(countReach(1));   // expected: 5 (B C A D E)
    41  }
    42}

    ▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.

    Show one possible answer
     1static int countReach(int u) {
     2  visited[u] = true;
     3  int count = 1;                      // u itself
     4  for (int v = 0; v < n; v++) {       // A to Z order
     5    if (adj[u][v] == 1 && !visited[v]) {
     6      count = count + countReach(v);  // go deeper
     7    }
     8  }
     9  return count;
    10}

    Output, one per line: 5, 3, 1, 5.

    Here is the first test, from A. The deepest calls finish first:

    CallWhyReturns
    countReach(C)C's only neighbor, A, is already visited1
    countReach(B)B itself + C1 + 1 = 2
    countReach(E)no arrows go out of E1
    countReach(D)D itself + E1 + 1 = 2
    countReach(A)A itself + B's 2 + D's 21 + 2 + 2 = 5

    The last test starts at B and also reaches 5: B → C → A, then A → D → E. The arrow C → A leads back to A. Only F is missed, because no arrow points into F.

    Here are two common mistakes:

    • Writing only countReach(v);. The call still runs, but the number it gives back is thrown away. Every call returns 1, so every test prints 1.
    • Forgetting visited[u] = true. Then DFS goes A → B → C → A → B → … around the cycle forever. Each call waits for the next one, and Java's call stack fills up: the program stops with StackOverflowError.

    Part 8 · Check yourself

    Self-check

    Not graded. Tap an answer to see if you got it, and read why.

    1. Which data structure does DFS use to remember where to go back to?

    2. In DFS, when the vertex on top of the stack has no unvisited neighbors, you:

    3. Main example, starting at A (alphabetical order). What is the DFS order?

    4. In the directed example, which vertices are not visited by DFS starting at F?

    5. DFS reaches vertex G. What do the vertices on the stack (bottom to top) form?

    6. You need the route with the fewest hops between two computers in a network. Which is the better choice?

    Score: 0 / 6

    Key takeaways

    1. DFS goes as deep as possible, then backtracks at dead ends.
    2. It uses a stack (or recursion). Push = go deeper; pop = go back.
    3. Write a vertex in the order when you first visit it.
    4. DFS finds everything reachable and can find a path, but not always the shortest.
    5. BFS = broad, queue, shortest path. DFS = deep, stack, follows one path to its end before trying another.

    Next unit: Sets in Java.

    References

    1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed. MIT Press, 2022, Section 20.3 (Depth-first search).
    2. Programiz, "Depth First Search (DFS) Algorithm." programiz.com/dsa/graph-dfs
    3. A. Bhargava, Grokking Algorithms. Manning, 2016, Chapter 6 (graphs and BFS, for comparison).