Rat in a Maze Problem in DSA Rust - Time & Space Complexity
We want to understand how the time needed to solve the Rat in a Maze problem changes as the maze size grows.
Specifically, how does the search for a path through the maze take longer when the maze gets bigger?
Analyze the time complexity of the following code snippet.
fn solve_maze(maze: &Vec>, x: usize, y: usize, solution: &mut Vec>) -> bool {
let n = maze.len();
if x == n - 1 && y == n - 1 {
solution[x][y] = 1;
return true;
}
if x < n && y < n && maze[x][y] == 1 {
solution[x][y] = 1;
if solve_maze(maze, x + 1, y, solution) {
return true;
}
if solve_maze(maze, x, y + 1, solution) {
return true;
}
solution[x][y] = 0;
return false;
}
false
}
This code tries to find a path from the top-left to the bottom-right of a maze using backtracking.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls exploring possible paths in the maze.
- How many times: Potentially, each cell is visited multiple times as the function tries different paths.
As the maze size (n x n) grows, the number of possible paths grows very fast because the rat can move right or down at each step.
| Input Size (n) | Approx. Operations |
|---|---|
| 2 | About 2 paths to check |
| 4 | About 20 paths to check |
| 10 | Thousands of paths to check |
Pattern observation: The number of paths grows exponentially as the maze size increases.
Time Complexity: O(2^{2n})
This means the time needed roughly quadruples with each increase in maze size n, making it very slow for large mazes.
[X] Wrong: "The rat only moves right or down, so the time grows linearly with maze size."
[OK] Correct: The rat tries many possible paths, including backtracking, so the number of paths grows exponentially, not linearly.
Understanding this problem helps you think about how recursive searches explore many possibilities and why some problems become very slow as input grows.
"What if we allowed the rat to move in all four directions instead of just right and down? How would the time complexity change?"
Practice
Rat in a Maze problem in data structures?Solution
Step 1: Understand the problem goal
The Rat in a Maze problem asks to find a path from the start cell (usually top-left) to the destination cell (usually bottom-right) in a maze represented by a grid.Step 2: Identify the correct goal
The goal is not counting cells, sorting, or shortest distance but finding a valid path through allowed cells.Final Answer:
Find a path from the start to the destination in a maze -> Option AQuick Check:
Goal = Find path [OK]
- Confusing pathfinding with counting cells
- Thinking it finds shortest path always
- Mixing it with sorting or graph distance problems
Solution
Step 1: Check parameter types for maze and coordinates
The maze is a 2D grid, so it should be a reference to a vector of vectors of integers (&Vec). Coordinates are usize for indexing.Step 2: Check return type and path parameter
The function returns bool to indicate success and uses a mutable path vector to store the route.Final Answer:
fn solve_maze(maze: &Vec, x: usize, y: usize, path: &mut Vec<(usize, usize)>) -> bool -> Option DQuick Check:
Correct signature uses references and bool return [OK]
- Passing maze by value causing unnecessary copying
- Using wrong coordinate types (i32 instead of usize)
- Returning wrong types like i32 or Vec instead of bool
fn main() {
let maze = vec![
vec![1, 0, 0, 0],
vec![1, 1, 0, 1],
vec![0, 1, 0, 0],
vec![1, 1, 1, 1],
];
let mut path = Vec::new();
if solve_maze(&maze, 0, 0, &mut path) {
println!("Path found: {:?}", path);
} else {
println!("No path found");
}
}
fn solve_maze(maze: &Vec, x: usize, y: usize, path: &mut Vec<(usize, usize)>) -> bool {
let n = maze.len();
if x == n - 1 && y == n - 1 {
path.push((x, y));
return true;
}
if x < n && y < n && maze[x][y] == 1 {
path.push((x, y));
if x + 1 < n && solve_maze(maze, x + 1, y, path) {
return true;
}
if y + 1 < n && solve_maze(maze, x, y + 1, path) {
return true;
}
path.pop();
}
false
}Solution
Step 1: Trace the recursive calls from (0,0)
Start at (0,0) which is 1 (allowed). Try moving down to (1,0) which is 1, then (2,0) is 0 (blocked), so backtrack. Then try right from (1,0) to (1,1) which is 1.Step 2: Continue path to destination
From (1,1), down to (2,1) is 1, then down to (3,1) is 1, right to (3,2) is 1, and right to (3,3) is 1 (destination). Path is [(0,0),(1,0),(1,1),(2,1),(3,1),(3,2),(3,3)].Final Answer:
Path found: [(0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3)] -> Option AQuick Check:
Recursive path = Path found: [(0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3)] [OK]
- Assuming path goes right first instead of down
- Not backtracking correctly on blocked cells
- Confusing coordinates order in path
fn solve_maze(maze: &Vec, x: usize, y: usize, path: &mut Vec<(usize, usize)>) -> bool {
let n = maze.len();
if x == n - 1 && y == n - 1 {
path.push((x, y));
return true;
}
if x < n && y < n && maze[x][y] == 1 {
path.push((x, y));
if solve_maze(maze, x + 1, y, path) {
return true;
}
if solve_maze(maze, x, y + 1, path) {
return true;
}
path.pop();
}
false
}Solution
Step 1: Analyze recursive calls for boundary safety
The recursive calls use x+1 and y+1 without checking if they exceed maze size, risking out-of-bounds panic.Step 2: Fix by adding boundary checks before recursion
Adding checks like x + 1 < n and y + 1 < n before calling solve_maze prevents invalid indexing.Final Answer:
Add boundary checks before recursive calls to avoid out-of-bounds -> Option CQuick Check:
Boundary checks prevent panic [OK]
- Ignoring index bounds causing runtime panic
- Changing maze to mutable unnecessarily
- Removing path.pop() causing wrong paths
Solution
Step 1: Understand requirement to find all paths
Finding all paths means exploring every possible route without stopping at the first success.Step 2: Use recursion to explore all moves and collect paths
Return a vector of paths and do not return early. Backtrack properly to explore all options.Final Answer:
Return a vector of paths and explore all possible moves recursively without stopping at first success -> Option BQuick Check:
Explore all paths recursively [OK]
- Stopping recursion early losing other paths
- Using BFS only for shortest path, not all paths
- Marking visited permanently blocking other paths
Ready to prove it?
Pass the test and this pattern moves unknown
