BackTrackingProblem 1 of 19

Rat in a maze Problem

Brute Force
Time: O(4^(n²))
Space: O(n²)
Optimal
Time: O(4^(n²))
Space: O(n²)

Problem Statement

Given a N x N maze with 0s and 1s, where 0 represents a blocked cell and 1 represents an open cell. A rat starts from the source (0, 0) and has to reach the destination (N-1, N-1). The rat can move in four directions: Up (U), Down (D), Left (L), Right (R). Find all possible paths from source to destination.

Example:

Input: maze = [ [1, 0, 0, 0], [1, 1, 0, 1], [1, 1, 0, 0], [0, 1, 1, 1] ] Output: ["DDRDRR", "DRDDRR"] Explanation: Path 1: Down -> Down -> Right -> Down -> Right -> Right Path 2: Down -> Right -> Down -> Down -> Right -> Right

Approach 1: Brute Force (Simple Recursion)

Intuition

Try all possible paths from source to destination by exploring all four directions at each cell. Mark cells as visited to avoid cycles, and backtrack when a path doesn't lead to the destination.

Algorithm

  1. Start from (0, 0) and explore all four directions
  2. For each direction, check if the move is valid (within bounds, cell is 1, not visited)
  3. If valid, mark as visited, add direction to path, and recurse
  4. If destination reached, store the path
  5. Backtrack by unmarking the cell
java
import java.util.*;

class Solution {
    public void solve(int row, int col, int[][] maze, int n, 
                      String path, List<String> result) {
        // Base case: reached destination
        if (row == n - 1 && col == n - 1) {
            result.add(path);
            return;
        }
        
        // Mark current cell as visited
        maze[row][col] = 0;
        
        // Try all four directions: Down, Left, Right, Up
        // Down
        if (row + 1 < n && maze[row + 1][col] == 1) {
            solve(row + 1, col, maze, n, path + 'D', result);
        }
        // Left
        if (col - 1 >= 0 && maze[row][col - 1] == 1) {
            solve(row, col - 1, maze, n, path + 'L', result);
        }
        // Right
        if (col + 1 < n && maze[row][col + 1] == 1) {
            solve(row, col + 1, maze, n, path + 'R', result);
        }
        // Up
        if (row - 1 >= 0 && maze[row - 1][col] == 1) {
            solve(row - 1, col, maze, n, path + 'U', result);
        }
        
        // Backtrack
        maze[row][col] = 1;
    }
    
    public List<String> findPath(int[][] maze, int n) {
        List<String> result = new ArrayList<>();
        if (maze[0][0] == 0 || maze[n-1][n-1] == 0) {
            return result;
        }
        solve(0, 0, maze, n, "", result);
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(4^(n²)) - At each cell, we have 4 choices
  • Space Complexity: O(n²) - Recursion stack depth can go up to n² in worst case

Approach 2: Optimal (Using Direction Arrays)

Intuition

Use direction arrays to make the code cleaner and more maintainable. This approach is essentially the same time complexity but with cleaner implementation.

Algorithm

  1. Use direction arrays for movement (dr, dc) and direction characters
  2. Iterate through all directions using a loop
  3. This makes it easier to add or modify directions
java
import java.util.*;

class Solution {
    private int[] dr = {1, 0, 0, -1};  // Down, Left, Right, Up
    private int[] dc = {0, -1, 1, 0};
    private char[] dir = {'D', 'L', 'R', 'U'};
    
    public void solve(int row, int col, int[][] maze, int n, 
                      String path, List<String> result, boolean[][] visited) {
        // Base case: reached destination
        if (row == n - 1 && col == n - 1) {
            result.add(path);
            return;
        }
        
        // Try all four directions
        for (int i = 0; i < 4; i++) {
            int newRow = row + dr[i];
            int newCol = col + dc[i];
            
            if (newRow >= 0 && newRow < n && newCol >= 0 && newCol < n &&
                maze[newRow][newCol] == 1 && !visited[newRow][newCol]) {
                visited[row][col] = true;
                solve(newRow, newCol, maze, n, path + dir[i], result, visited);
                visited[row][col] = false;
            }
        }
    }
    
    public List<String> findPath(int[][] maze, int n) {
        List<String> result = new ArrayList<>();
        boolean[][] visited = new boolean[n][n];
        
        if (maze[0][0] == 0 || maze[n-1][n-1] == 0) {
            return result;
        }
        
        solve(0, 0, maze, n, "", result, visited);
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(4^(n²)) - At each cell, we have 4 choices
  • Space Complexity: O(n²) - For visited array and recursion stack

Key Takeaways

  1. Backtracking Pattern: Mark visited, explore, unmark (backtrack)
  2. Direction Arrays: Using dr[], dc[] arrays makes code cleaner and extensible
  3. Lexicographical Order: Direction order (D, L, R, U) ensures lexicographically sorted paths
  4. Early Termination: Check if start/end cells are blocked before starting
  5. Visited Tracking: Essential to avoid infinite loops in paths that can revisit cells
  6. Classic Backtracking: Foundation for understanding more complex maze/path problems