63. Unique Paths II

#dp

Problem

Intuition

Intuition

Time Complexity

Space Complexity

Solution

class Solution {
    public int uniquePathsWithObstacles(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int[][] dp = new int[m][n];

        return tabulationSpaceOptimisedHelper(grid);
    }

    /**
     * Calculates the number of unique paths using a tabulation (bottom-up) approach using obstalce grid itself.
     *
     * Time complexity: O(m * n), where m is the number of rows and n is the number of columns in the grid.
     * Space complexity: O(1), No extra space is used, making use of input matrix only to store calculations
    */
    public int tabulationSpaceOptimisedHelper(int[][] obstacleGrid) {
        int R = obstacleGrid.length; // Number of rows
        int C = obstacleGrid[0].length; // Number of columns
    
        // If the starting position is blocked, there are no paths possible.
        if (obstacleGrid[0][0] == 1) {
            return 0;
        }
    
        // Set the starting position as 1 to indicate that there is one path to reach it.
        obstacleGrid[0][0] = 1;
    
        // Calculate the number of paths for the first row.
        for (int col = 1; col < C; col++) {
            // If the current cell is not an obstacle and the cell on the left has a valid path, 
            // set the current cell's path count to 1.
            if (obstacleGrid[0][col] == 0 && obstacleGrid[0][col - 1] == 1) {
                obstacleGrid[0][col] = 1;
            } else {
                // Otherwise, set the current cell's path count to 0.
                obstacleGrid[0][col] = 0;
            }
        }
    
        // Calculate the number of paths for the first column.
        for (int row = 1; row < R; row++) {
            // If the current cell is not an obstacle and the cell above has a valid path, 
            // set the current cell's path count to 1.
            if (obstacleGrid[row][0] == 0 && obstacleGrid[row - 1][0] == 1) {
                obstacleGrid[row][0] = 1;
            } else {
                // Otherwise, set the current cell's path count to 0.
                obstacleGrid[row][0] = 0;
            }
        }
    
        // Calculate the number of paths for the remaining cells.
        for (int row = 1; row < R; row++) {
            for (int col = 1; col < C; col++) {
                if (obstacleGrid[row][col] == 0) {
                    // If the current cell is not an obstacle, calculate the number of paths as the sum
                    // of paths from the above cell and the left cell.
                    obstacleGrid[row][col] = obstacleGrid[row - 1][col] + obstacleGrid[row][col - 1];
                } else {
                    // If the current cell is an obstacle, set the number of paths to reach it as 0.
                    obstacleGrid[row][col] = 0;
                }
            }
        }

        // Return the number of paths to reach the bottom-right corner.
        return obstacleGrid[R - 1][C - 1];
    }


    /**
     * Calculates the number of unique paths using a tabulation (bottom-up) approach.
     *
     * Time complexity: O(m * n), where m is the number of rows and n is the number of columns in the grid.
     * Space complexity: O(m * n), for the dp array to store intermediate results.
     */
    private int tabulationHelper(int[][] grid, int[][] dp) {
        if(grid[0][0] == 1) {
            return 0;
        }

        dp[0][0] = 1;
        int R = grid.length, C = grid[0].length;

        // Calculate the number of paths for the first column
        for(int row = 1; row < R; row++) {
            if(grid[row][0] == 0 && dp[row-1][0] == 1) {
                dp[row][0] = 1;
            }
        }

        // Calculate the number of paths for the first row
        for(int col = 1; col < C; col++) {
            if(grid[0][col] == 0 && dp[0][col-1] == 1) {
                dp[0][col] = 1;
            }
        }

        // Calculate the number of paths for the remaining cells
        for(int row = 1; row < R; row++) {
            for(int col = 1; col < C; col++) {
                if(grid[row][col] == 0) {
                    int up = dp[row-1][col];
                    int left = dp[row][col-1];
                    dp[row][col] = up + left;
                }
            }
        }
        return dp[R-1][C-1];
    }

    /**
     * Calculates the number of unique paths using a memoization (top-down) approach.(TLE)
     *
     * Time complexity: O(m * n), where m is the number of rows and n is the number of columns in the grid.
     * Space complexity: O(m * n), for the dp array to store intermediate results.
     */
    private int memoizationHelper(int i, int j, int[][] grid, int[][] dp) {
        // If we go out of bounds or encounter an obstacle, return 0 as there are no paths.
        if(i < 0 || j < 0 || grid[i][j] == 1) {
            return 0;
        }

        // If we reach the top-left corner, return 1 as we have found a unique path.
        if(i == 0 && j == 0) {
            return 1;
        }

        // If the result for the current position is already computed, return it from the dp array.
        if(dp[i][j] != 0) {
            return dp[i][j];
        }

        // Calculate the number of paths by recursively moving either up or left.
        int up = memoizationHelper(i-1, j, grid, dp);
        int left = memoizationHelper(i, j-1, grid, dp);

        // Store the computed result in the dp array and return it.
        return dp[i][j] = up + left;
    }

    /**
     * Calculates the number of unique paths using a recursive approach.(TLE)
     *
     * Time complexity: O(2^(m + n)), where m is the number of rows and n is the number of columns in the grid.
     * Space complexity: O(m + n), for the recursion stack.
     */
    private int recursiveHelper(int i, int j, int[][] grid) {
        // If we reach the top-left corner, return 1 as we have found a unique path.
        if(i == 0 && j == 0) {
            return 1;
        }

        // If we go out of bounds or encounter an obstacle, return 0 as there are no paths.
        if(i < 0 || j < 0 || grid[i][j] == 1) {
            return 0;
        }

        // Calculate the number of paths by recursively moving either up or left.
        int up = recursiveHelper(i-1, j, grid);
        int left = recursiveHelper(i, j-1, grid);

        // Return the sum of paths from the current position.
        return up + left;
    }
}

Last updated

Was this helpful?