Number of Islands BFS and DFS in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find islands grows as the map size grows.
How does the search through the grid affect the total work done?
Analyze the time complexity of the following code snippet.
fn num_islands(grid: &mut Vec>) -> i32 {
let rows = grid.len();
let cols = grid[0].len();
let mut count = 0;
for r in 0..rows {
for c in 0..cols {
if grid[r][c] == '1' {
dfs(grid, r, c);
count += 1;
}
}
}
count
}
fn dfs(grid: &mut Vec>, r: usize, c: usize) {
if r >= grid.len() || c >= grid[0].len() || grid[r][c] == '0' {
return;
}
grid[r][c] = '0';
if r > 0 { dfs(grid, r - 1, c); }
if c > 0 { dfs(grid, r, c - 1); }
if r + 1 < grid.len() { dfs(grid, r + 1, c); }
if c + 1 < grid[0].len() { dfs(grid, r, c + 1); }
}
This code counts islands by searching connected land cells using depth-first search.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each cell once during DFS calls.
- How many times: Each cell is checked once in the nested loops and visited once in DFS.
As the grid size grows, the code visits each cell once, so work grows directly with the number of cells.
| Input Size (n = rows * cols) | Approx. Operations |
|---|---|
| 10 | About 10 visits |
| 100 | About 100 visits |
| 1000 | About 1000 visits |
Pattern observation: The operations grow linearly with the total number of cells.
Time Complexity: O(rows * cols)
This means the time needed grows directly with the total number of cells in the grid.
[X] Wrong: "DFS runs multiple times on the same cells, so time is more than linear."
[OK] Correct: Each cell is marked visited ('0') after first visit, so DFS never revisits it, keeping total work linear.
Understanding this helps you explain how searching connected parts in grids works efficiently, a common real-world problem.
"What if we used a queue for BFS instead of recursion for DFS? How would the time complexity change?"
Practice
Solution
Step 1: Understand the grid content
The grid contains '1's representing land and '0's representing water.Step 2: Define the problem goal
The problem asks to count how many separate groups of connected '1's exist, called islands.Final Answer:
Count how many groups of connected '1's (islands) are in the grid -> Option CQuick Check:
Number of islands = count of connected '1' groups [OK]
- Confusing island count with largest island size
- Counting '0's instead of '1's
- Calculating perimeter instead of count
Solution
Step 1: Identify correct data type for grid
The grid should be a 2D vector of chars because the problem uses '1' and '0' as characters.Step 2: Check syntax for 2D vector initialization
let grid: Vec = vec![vec!['1', '0'], vec!['0', '1']]; uses Vec with vec! macro and char literals '1' and '0', which is correct Rust syntax.Final Answer:
let grid: Vec = vec![vec!['1', '0'], vec!['0', '1']]; -> Option BQuick Check:
2D vector of chars with vec! and '1'/'0' chars [OK]
- Using integer types instead of char
- Using arrays without specifying type
- Using 1D vector instead of 2D
fn main() {
let mut grid = vec![
vec!['1', '1', '0', '0'],
vec!['1', '0', '0', '1'],
vec!['0', '0', '1', '1'],
vec!['0', '0', '0', '0'],
];
println!("{}", num_islands(&mut grid));
}
fn num_islands(grid: &mut Vec) -> i32 {
let mut count = 0;
for i in 0..grid.len() {
for j in 0..grid[0].len() {
if grid[i][j] == '1' {
dfs(grid, i, j);
count += 1;
}
}
}
count
}
fn dfs(grid: &mut Vec, i: usize, j: usize) {
if i >= grid.len() || j >= grid[0].len() || grid[i][j] == '0' {
return;
}
grid[i][j] = '0';
if i > 0 { dfs(grid, i - 1, j); }
dfs(grid, i + 1, j);
if j > 0 { dfs(grid, i, j - 1); }
dfs(grid, i, j + 1);
}Solution
Step 1: Identify islands in the grid
The grid has these islands: - Top-left corner: (0,0),(0,1),(1,0) - Single '1' at (1,3) - Connected '1's at (2,2),(2,3)Step 2: Count islands after DFS marks visited
DFS marks connected '1's as '0' to avoid recounting. Total islands found = 3.Final Answer:
3 -> Option DQuick Check:
Count connected groups = 3 [OK]
- Counting diagonal connections as islands
- Not marking visited cells
- Off-by-one errors in loops
fn dfs(grid: &mut Vec, i: usize, j: usize) {
if i > grid.len() || j > grid[0].len() || grid[i][j] == '0' {
return;
}
grid[i][j] = '0';
if i > 0 { dfs(grid, i - 1, j); }
dfs(grid, i + 1, j);
if j > 0 { dfs(grid, i, j - 1); }
dfs(grid, i, j + 1);
}Solution
Step 1: Analyze boundary condition in DFS
The code checks if i > grid.len() or j > grid[0].len(), but valid indices go up to len()-1, so i == len() is out-of-bounds.Step 2: Correct boundary check to prevent panic
Use i >= grid.len() and j >= grid[0].len() to avoid accessing invalid indices and crashing.Final Answer:
The boundary check uses '>' instead of '>=' causing out-of-bounds panic -> Option AQuick Check:
Use >= for boundary checks to avoid panic [OK]
- Using > instead of >= in boundary checks
- Forgetting to mark visited cells
- Ignoring out-of-bounds errors
Solution
Step 1: Understand BFS traversal for islands
BFS visits all connected '1's in an island. Counting visited nodes during BFS gives island size.Step 2: Track and update largest island size
Maintain a counter inside BFS for current island size, update max size after BFS completes for each island.Final Answer:
Add a counter inside BFS to count connected '1's and track max size -> Option AQuick Check:
Count nodes during BFS to find largest island size [OK]
- Counting '1's before BFS (not island size)
- Using DFS without counting size
- Returning count of '0's instead of island size
Ready to prove it?
Pass the test and this pattern moves unknown
