LeetCode:63. 不同路径

99 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

63. 不同路径II

来源:力扣(LeetCode)

链接: leetcode.cn/problems/un… 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

在这里插入图片描述

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

在这里插入图片描述

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1

解法

  • 动态规划:四个步骤:
    • 问题定义
    • 状态转移方程
    • 初始条件和边界情况
    • 确定计算顺序(自顶向下,还是自下向上)

问题定义: 机器人走到最后一个格子的次数,由于机器人只能往右往下方向移动,要求最后一个格子的所有路径。如果我们知道了该格子上面的那个格子的路径总数,以及格子左边格子的路径总数,最后一个格子的路径总数等于上面那个格子以及左边那个格子的路径和,类似于斐波拉契数列f(n)=f(n1)+f(n2)f(n) = f(n-1) + f(n-2) ,这里可以用递归方式,可以用动态规划方式,由于递归的复杂度太高,这里用动态规划进行解决。同时也要注意有障碍物的问题,如果该位置是障碍物,则路径和为0;

状态转移方程: 但这里是二维的,可以用一个二维数组定义该问题

  • dp[i][i] = dp[i][j-1] + dp[i-1][j], i,j > 0 & obs[i][j] == 0
  • dp[i][j]=0,obs[i][j]==1dp[i][j] = 0, obs[i][j] == 1

初始化条件和边界条件 在这里插入图片描述 由于只能向右和向下移动,所以第一行都只有一种路径方式到达,第一列也是只有一种路径方式达到,所以初始化

  • dp[0][0] = 1 if if obstacleGrid[i][0] != 1 else 0
  • dp[i][0] = dp[i-1][0] if obstacleGrid[i][0] != 1 else 0
  • dp[0][j] = dp[0][j-1] if obstacleGrid[0][j] != 1 else 0

确定计算顺序: 这个是从下向上的方向计算即可

代码实现

动态规划

python实现

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])

        dp = [[0] * n for _ in range(m)]
        if obstacleGrid[0][0] == 1:
            dp[0][0] = 0
        else:
            dp[0][0] = 1

        for i in range(1, m):  # 初始化第一列
            dp[i][0] = dp[i-1][0] if obstacleGrid[i][0] != 1 else 0

        for j in range(1, n):  # 初始化第一行
            dp[0][j] = dp[0][j-1] if obstacleGrid[0][j] != 1 else 0

        for i in range(1, m):
            for j in range(1, n):
                if obstacleGrid[i][j] == 1:
                    dp[i][j] = 0
                else:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]

c++实现

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int> (n));
        if (obstacleGrid[0][0] == 1)
            dp[0][0] = 0;
        else
            dp[0][0] = 1;
        
        for (int i=1; i<m; i++) {
            if (obstacleGrid[i][0] != 1)
                dp[i][0] = dp[i-1][0];
            else
                dp[i][0] = 0;
        }

        for (int j=1; j<n; j++) {
            if (obstacleGrid[0][j] != 1)
                dp[0][j] = dp[0][j-1];
            else
                dp[0][j] = 0;
        }
        
        for (int i=1; i<m; i++) {
            for (int j=1; j<n; j++) {
                if (obstacleGrid[i][j] != 1) {
                    dp[i][j] = dp[i][j-1] + dp[i-1][j];
                }
                else
                    dp[i][j] = 0;
            }
        }
        return dp[m-1][n-1];

    }
};

复杂度分析

  • 时间复杂度: O(MN)O(MN)
  • 空间复杂度: O(MN)O(MN)

优化

dp[i][j] = dp[i-1][j] + dp[i][j-1] --> cur[j] = cur[j-1] + pre[j] 由于每次更新的时候只与当前行和最上面一行有关系,我们可以将存储整个二维矩阵替换成只保存当前层和上一层,空间复杂度从O(MN)O(MN)变成O(2N)O(2N), 这里要注意初始化的问题

python实现

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])

        pre_row = [0] * n
        cur_row = [0] * n

        if obstacleGrid[0][0] == 0:
            cur_row[0] = 1
        else:
            return 0
        
        # 处理第一行
        for i in range(1, n):
            if obstacleGrid[0][i] == 1:
                cur_row[i] = 0
            else:
                cur_row[i] = cur_row[i-1]
        
        # 开始赋值给第一行
        pre_row = cur_row

        for i in range(1, m):
            for j in range(0, n):
                if obstacleGrid[i][j] == 1:
                    cur_row[j] = 0
                else:
                    if j > 0:
                        cur_row[j] = cur_row[j-1] + pre_row[j]
                    else:
                        cur_row[j] = pre_row[j]
            pre_row = cur_row
        return pre_row[-1]

c++实现

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();

        vector<int> pre_row (n, 0);
        vector<int> cur_row (n, 0);
        if (obstacleGrid[0][0] == 1)
            return 0;
        
        cur_row[0] = 1;
        // 处理第一行
        for (int i=1; i<n; i++) {
            if (obstacleGrid[0][i] == 0) {
                cur_row[i] = cur_row[i-1];
            }
        }
        pre_row = cur_row;

        
        for (int i=1; i<m; i++) {
            for (int j=0; j<n; j++) {
                if (obstacleGrid[i][j] == 1)
                    cur_row[j] = 0;
                else {
                    if (j > 0)
                        cur_row[j] = cur_row[j-1] + pre_row[j];
                    else
                        cur_row[j] = pre_row[j];
                }
            }
            pre_row = cur_row;
        }
        return pre_row[n-1];
    }
};

复杂度分析

  • 时间复杂度: O(MN)O(MN)
  • 空间复杂度: O(2N)O(2N)

继续优化

我们可以将其压缩为一行, cur[j] = cur[j-1] + pre[j] -> cur[j] = cur[j] + cur[j-1]

每次都将新的结果重新设置给每一列即可,新的结果只与左边那一列有关系, 空间复杂度降低为O(N)O(N) python实现

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])

        if obstacleGrid[0][0] == 1:
            return 0
        
        dp = [0] * n
        dp[0] = 1
        for i in range(m):
            for j in range(n):
                if obstacleGrid[i][j] == 1:
                    dp[j] = 0
                    continue
                if j > 0 and obstacleGrid[i][j-1] == 0:
                    dp[j] = dp[j] + dp[j-1]
        return dp[-1]

c++实现

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<int> dp (n, 0);
        if (obstacleGrid[0][0] == 1) return 0;

        dp[0] = 1;
        for (int i=0; i<m; i++) {
            for (int j=0; j<n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                    continue;
                }
                if (j > 0 && obstacleGrid[i][j-1] == 0)
                    dp[j] = dp[j] + dp[j-1];
            }
        }
        return dp[n-1];
    }
};

复杂度分析

  • 时间复杂度: O(MN)O(MN)
  • 空间复杂度: O(N)O(N)