BFS vs DFS in Graph Traversal: Key Differences Explained Using the LeetCode-Master Repository
BFS explores nodes layer-by-layer using a queue to guarantee shortest paths in unweighted graphs, while DFS dives deep into each branch using recursion before backtracking, making them suited for different problem types in the leetcode-master codebase.
The youngyangyang04/leetcode-master repository provides canonical templates that demonstrate exactly how Breadth-First Search (BFS) and Depth-First Search (DFS) differ in implementation, performance, and application. Understanding these BFS and DFS differences is essential for solving graph problems ranging from counting islands to finding shortest paths.
Search Order and Data Structures
The fundamental distinction between these algorithms lies in how they explore the graph frontier.
BFS: Layer-by-Layer with a Queue
BFS utilizes a FIFO queue to process all nodes at the current distance before moving outward. According to the repository’s BFS theory file (problems/kamacoder/图论广搜理论基础.md), this creates a "ring expansion" effect that naturally finds the shortest path in unweighted graphs.
The C++ template uses queue<pair<int,int>> to store coordinates, while the Python implementation relies on collections.deque. Both versions iterate through four directional offsets defined as dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1} in C++ or equivalent tuples in Python.
DFS: Deep-First with Recursion or Stack
DFS follows a single path to its conclusion using recursion (implicit call stack) or an explicit LIFO stack. As implemented in problems/kamacoder/图论深搜理论基础.md, DFS explores one branch completely before backtracking, creating a "trail" visualization rather than concentric rings.
The repository’s DFS templates use plain recursion in both C++ and Python, with the base case being boundary checks or visited constraints.
When to Use BFS vs DFS
The leetcode-master solutions demonstrate clear problem-type boundaries for selecting each algorithm.
Shortest Path and Level-Order Problems
Use BFS when the problem requires the minimum number of steps, shortest distance, or level-order traversal. The repository’s solution for "Number of Islands" (problems/0200.岛屿数量.广搜版.md) uses BFS to demonstrate shortest-path properties, though the problem itself only requires counting components.
Other BFS-appropriate scenarios include:
- Word Ladder problems
- Shortest path in unweighted grids
- Binary tree level-order traversal (as seen in
problems/0102.二叉树的层序遍历.md)
Component Size and Reachability Problems
Use DFS when you need to explore the complete extent of a component, determine reachability, or count connected regions where path length is irrelevant. The DFS version of "Number of Islands" (problems/0200.岛屿数量.深搜版.md) provides a more concise implementation for simply marking visited land cells.
DFS excels in:
- Maximum area of island calculations
- Connected component labeling
- Reachability checks (
problems/kamacoder/0105.有向图的完全可达性.mdsupports both approaches)
Critical Implementation Details
The repository emphasizes specific implementation quirks that affect performance and correctness.
The "Mark When Enqueued" Rule for BFS
A crucial performance distinction appears in how each algorithm handles visited markers. The BFS template in problems/kamacoder/图论广搜理论基础.md (lines 73-85) marks nodes visited when they are enqueued, not when dequeued.
Marking after dequeuing causes duplicate pushes to the queue, leading to timeouts in large grids. The canonical BFS pattern:
void bfs(vector<vector<char>>& grid,
vector<vector<bool>>& visited,
int x, int y) {
queue<pair<int,int>> que;
que.push({x, y});
visited[x][y] = true; // Mark immediately
while (!que.empty()) {
auto cur = que.front();
que.pop();
// Process neighbors...
if (!visited[nx][ny] && grid[nx][ny] == '1') {
que.push({nx, ny});
visited[nx][ny] = true; // Mark immediately
}
}
}
DFS Recursion Pattern
The DFS implementation (problems/kamacoder/图论深搜理论基础.md, lines 71-78) marks visited before recursing to prevent cycles:
void dfs(vector<vector<char>>& grid,
vector<vector<bool>>& visited,
int x, int y) {
for (int i = 0; i < 4; ++i) {
int nx = x + dir[i][0];
int ny = y + dir[i][1];
// Boundary checks...
if (!visited[nx][ny] && grid[nx][ny] == '1') {
visited[nx][ny] = true; // Mark before recurse
dfs(grid, visited, nx, ny);
}
}
}
Space Complexity Trade-offs
Both algorithms require O(V) space for the visited set, but their auxiliary memory differs:
- BFS: Requires O(V) space for the queue in the worst case (storing the frontier). This becomes significant in wide, shallow graphs where many nodes are at the same distance level.
- DFS: Requires O(V) space for the recursion stack in the worst case (deep, linear chains). However, for balanced or sparse graphs, the stack depth remains O(log V) or O(branching factor), typically using less memory than BFS's queue.
Code Examples from the Repository
BFS Implementation (Number of Islands)
The Python BFS solution from problems/0200.岛屿数量.广搜版.md demonstrates the deque pattern:
from collections import deque
def bfs(grid, i, j, visited):
dirs = [(0,1), (1,0), (-1,0), (0,-1)]
q = deque()
q.append((i, j))
visited[i][j] = True # Mark when enqueued
while q:
x, y = q.popleft()
for dx, dy in dirs:
nx, ny = x + dx, y + dy
if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]):
if not visited[nx][ny] and grid[nx][ny] == '1':
q.append((nx, ny))
visited[nx][ny] = True # Mark immediately
DFS Implementation (Number of Islands)
The recursive DFS from problems/0200.岛屿数量.深搜版.md shows the backtracking approach:
def dfs(grid, x, y, visited):
dirs = [(0,1), (1,0), (-1,0), (0,-1)]
for dx, dy in dirs:
nx, ny = x + dx, y + dy
if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]):
if not visited[nx][ny] and grid[nx][ny] == '1':
visited[nx][ny] = True
dfs(grid, nx, ny, visited) # Recurse deeper
Summary
- BFS uses a queue and explores layer-by-layer, making it the correct choice for shortest-path problems in unweighted graphs according to the leetcode-master templates.
- DFS uses recursion to explore as deep as possible before backtracking, ideal for component size and reachability tasks where path length is irrelevant.
- Always mark nodes visited when enqueuing in BFS to avoid duplicate processing and timeouts.
- Both require O(V) visited space, but BFS may use more memory for the queue frontier while DFS risks stack overflow on deep graphs.
Frequently Asked Questions
Why does the LeetCode-Master repository mark nodes visited when enqueuing in BFS?
Marking visited at enqueue time prevents the same node from being added to the queue multiple times through different paths. If you mark only when dequeuing, a node could accumulate exponentially many pending entries in large grids, causing time-limit-exceeded errors. The repository explicitly warns against this anti-pattern in problems/kamacoder/图论广搜理论基础.md.
Can I use DFS for shortest path problems in unweighted graphs?
While DFS can technically find a path, it does not guarantee the shortest path because it explores one branch completely before trying alternatives. BFS inherently finds the shortest path in unweighted graphs because it explores all nodes at distance k before distance k+1. The repository reserves BFS for shortest-path questions and DFS for component analysis.
When should I prefer iterative DFS over recursive DFS?
Use iterative DFS (with an explicit stack) when the graph depth might exceed the language's recursion stack limit—typically around 10^4 levels in Python or C++. The repository generally uses recursive DFS for code clarity in interview settings, but notes that iterative versions are safer for extremely deep grids or trees to avoid stack overflow errors.
How do I choose between BFS and DFS for island problems like Number of Islands?
Both work correctly for simple counting, but the repository provides separate implementations to demonstrate the differences. Choose BFS if you later need to modify the solution to find the shortest bridge between islands or minimum distance to water. Choose DFS for cleaner code when you only need the count or maximum area, as the recursive implementation is typically more concise and easier to read.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →