Depth-First Search (DFS)
Depth-First Search (DFS) is a graph traversal algorithm that explores as deep as possible along each branch before backtracking. It behaves like a maze solver: it follows a single path to its end, backtracks when it hits a dead end, and then tries the next available branch.
Since graphs can contain cycles, DFS uses a visited tracker (an array or set) to prevent visiting the same node more than once.
How DFS Works (Step-by-Step)
DFS naturally uses a Stack (Last-In-First-Out) structure, which is typically implemented via the function call stack in a recursive implementation.
Visualizing the Graph
Let's perform DFS on the following undirected graph starting from vertex A:
DFS Trace Sequence
Starting from A, assuming we explore adjacent vertices in alphabetical order:
- Visit
A: MarkAvisited. Output:A. Stack:[A]. - Move to
B(neighbor ofA): MarkBvisited. Output:A B. Stack:[A, B]. - Move to
D(neighbor ofB): MarkDvisited. Output:A B D. Stack:[A, B, D]. - Move to
E(neighbor ofD): MarkEvisited. Output:A B D E. Stack:[A, B, D, E]. - Move to
F(neighbor ofE): MarkFvisited. Output:A B D E F. Stack:[A, B, D, E, F]. - Backtrack from
F: Neighbors ofF(B,C,E) are checked.BandEare already visited. We backtrack toE, then toD, then toB. - Backtrack to
A: FromA, we visitC(unvisited neighbor). MarkCvisited. Output:A B D E F C. Stack:[A, C]. - Final Traversal Order:
A -> B -> D -> E -> F -> C.
JavaScript Code Implementation
class Graph {
constructor() {
this.list = {};
}
addVertex(vertex) {
if (!this.list[vertex]) this.list[vertex] = [];
}
addEdge(v1, v2) {
this.addVertex(v1);
this.addVertex(v2);
this.list[v1].push(v2);
this.list[v2].push(v1);
}
// Depth-First Search (DFS) Entry Point
dfs(startNode) {
const visited = {};
const result = [];
this._dfsHelper(startNode, visited, result);
return result;
}
// Recursive DFS Helper
_dfsHelper(node, visited, result) {
// Mark current node as visited and add it to results
visited[node] = true;
result.push(node);
const neighbors = this.list[node] || [];
for (let neighbor of neighbors) {
// Recursively visit all unvisited neighbors
if (!visited[neighbor]) {
this._dfsHelper(neighbor, visited, result);
}
}
}
}
// Driver Code
const g = new Graph();
g.addEdge("A", "B");
g.addEdge("A", "C");
g.addEdge("B", "D");
g.addEdge("B", "F");
g.addEdge("C", "F");
g.addEdge("D", "E");
g.addEdge("E", "F");
console.log("DFS Traversal:", g.dfs("A").join(" "));
// Output: DFS Traversal: A B D E F CComplexity Analysis
- Time Complexity: $O(V + E)$
- Each vertex is visited exactly once: $O(V)$.
- We traverse the adjacency list of every vertex exactly once, visiting all edges: $O(E)$ (specifically $2E$ for undirected graphs).
- Space Complexity: $O(V)$
- The recursion stack can grow up to $V$ frames in the worst case (e.g., a straight line/skewed tree graph).
- The
visitedobject stores up to $V$ keys.