Depth-first search (DFS)

Follow one path to its end before backtracking — and use the recursion to detect cycles and order dependencies.

DFS commits to a path until it dead-ends, then backs up. Swap BFS's queue for a stack and you have it; write it recursively and the stack is implicit.

function dfs(graph, node, visited = new Set(), order = []) {
  visited.add(node);
  order.push(node);

  for (const next of graph.get(node) ?? []) {
    if (!visited.has(next)) dfs(graph, next, visited, order);
  }

  return order;
}

O(V + E) time, O(V) space — but that space is the call stack, so a graph with a long path can overflow it. The iterative form removes that risk:

function dfsIterative(graph, start) {
  const visited = new Set();
  const stack = [start];
  const order = [];

  while (stack.length) {
    const node = stack.pop();
    if (visited.has(node)) continue;      // mark on pop: a node may be stacked twice

    visited.add(node);
    order.push(node);

    for (const next of graph.get(node) ?? []) {
      if (!visited.has(next)) stack.push(next);
    }
  }

  return order;
}

Note the difference from BFS: here you check on pop, because a vertex can legitimately be pushed by several neighbours before being visited.

Cycle detection

For a directed graph, three colours: unvisited, in progress, finished. Meeting an in-progress vertex means a cycle — a back edge.

function hasCycle(graph) {
  const state = new Map();      // 0 unvisited, 1 in progress, 2 done

  const visit = (node) => {
    if (state.get(node) === 1) return true;    // back edge → cycle
    if (state.get(node) === 2) return false;   // already cleared

    state.set(node, 1);
    for (const next of graph.get(node) ?? []) if (visit(next)) return true;
    state.set(node, 2);

    return false;
  };

  return [...graph.keys()].some((node) => state.get(node) !== 2 && visit(node));
}

A plain visited set is not enough — it cannot tell "already finished" from "currently on the stack", and that distinction is the cycle.

For an undirected graph, ignore the edge you arrived on and any other visited neighbour is a cycle.

BFS or DFS?

Question Use
Shortest path, unweighted BFS
Fewest moves / minimum steps BFS
Does a path exist? Either — DFS uses less memory on wide graphs
Detect a cycle DFS
Topological order DFS or Kahn's BFS
Connected components, flood fill Either
All paths, all configurations DFS with backtracking