Depth-First Search (graph)
Algorithm · Core
Step through it: Depth-First Search pattern
start at 7
Step 1 of 6
Goal: visit every node. Depth-first search picks a direction and goes as deep as it can before turning back, like exploring a maze by always taking the next corridor.
Step 2 of 6
From the root, 7, go down the left side to 9.
Step 3 of 6
Keep going down to 5. It has no children: a dead end.
Step 4 of 6
Back up to 9 and try its other child, 2. Another dead end.
Step 5 of 6
Back up to the root and take the last branch: 11. Recursion remembers where to return to (a stack does the same). On a graph also keep a visited list so you never loop. This is depth-first search.
Step 6 of 6
Your turn.
Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. One starts at the root (selecting some arbitrary node as the root in the case of a graph) and explores as far as possible along each branch before backtracking.
Code
/**
* @typedef {Object} Callbacks
*
* @property {function(vertices: Object): boolean} [allowTraversal] -
* Determines whether DFS should traverse from the vertex to its neighbor
* (along the edge). By default prohibits visiting the same vertex again.
*
* @property {function(vertices: Object)} [enterVertex] - Called when DFS enters the vertex.
*
* @property {function(vertices: Object)} [leaveVertex] - Called when DFS leaves the vertex.
*/
/**
* @param {Callbacks} [callbacks]
* @returns {Callbacks}
*/
function initCallbacks(callbacks = {}) {
const initiatedCallback = callbacks;
const stubCallback = () => {};
const allowTraversalCallback = (
() => {
const seen = {};
return ({ nextVertex }) => {
if (!seen[nextVertex.getKey()]) {
seen[nextVertex.getKey()] = true;
return true;
}
return false;
};
}
)();
initiatedCallback.allowTraversal = callbacks.allowTraversal || allowTraversalCallback;
initiatedCallback.enterVertex = callbacks.enterVertex || stubCallback;
initiatedCallback.leaveVertex = callbacks.leaveVertex || stubCallback;
return initiatedCallback;
}
/**
* @param {Graph} graph
* @param {GraphVertex} currentVertex
* @param {GraphVertex} previousVertex
* @param {Callbacks} callbacks
*/
function depthFirstSearchRecursive(graph, currentVertex, previousVertex, callbacks) {
callbacks.enterVertex({ currentVertex, previousVertex });
graph.getNeighbors(currentVertex).forEach((nextVertex) => {
if (callbacks.allowTraversal({ previousVertex, currentVertex, nextVertex })) {
depthFirstSearchRecursive(graph, nextVertex, currentVertex, callbacks);
}
});
callbacks.leaveVertex({ currentVertex, previousVertex });
}
/**
* @param {Graph} graph
* @param {GraphVertex} startVertex
* @param {Callbacks} [callbacks]
*/
export default function depthFirstSearch(graph, startVertex, callbacks) {
const previousVertex = null;
depthFirstSearchRecursive(graph, startVertex, previousVertex, initCallbacks(callbacks));
}References
Practice
Depth-First Search
- Maximum Depth of Binary TreeEasy
- Path SumEasy
- Diameter of Binary TreeEasy
- Balanced Binary TreeEasy
- Find if Path Exists in GraphEasy
- Path Sum IIMedium
- Path Sum IIIMedium
- Clone GraphMedium
- Number of ProvincesMedium
- All Paths From Source to TargetMedium
- Course ScheduleMedium
- Pacific Atlantic Water FlowMedium
- Count Good Nodes in Binary TreeMedium
- Sum Root to Leaf NumbersMedium
- Reconstruct ItineraryMedium
- Is Graph Bipartite?Medium
- Binary Tree Maximum Path SumHard
- Longest Increasing Path in a MatrixHard
Spotted a mistake or something unclear? Report it on GitHub.