LeetCode Hot100(62/100)——62. 不同路径

文章目录

题目理解

我们可以把网格看成一个棋盘:

  • 起点:左上角
  • 终点:右下角
  • 允许动作:向右向下

m = 3, n = 7 为例,答案是 28

一个直观网格示意:

0 1 2 3 4 5 6
0 S
1
2 E

解法总览

不同路径
暴力递归
思路直观
指数级复杂度
记忆化搜索
递归 + 缓存
避免重复子问题
动态规划
二维DP
状态转移清晰
一维DP
空间优化
数学组合
本质是排列组合
复杂度最低


关键状态与转移

定义 f(i, j) 表示从起点走到格子 (i,j) 的路径数。

因为最后一步只能来自:

  • 上方 (i-1, j)(向下走一步到当前)
  • 左方 (i, j-1)(向右走一步到当前)

所以有经典转移方程:

f ( i , j ) = f ( i − 1 , j ) + f ( i , j − 1 ) f(i,j)=f(i-1,j)+f(i,j-1) f(i,j)=f(i−1,j)+f(i,j−1)

边界:

  • 第一行只能一直向右走,所以都是 1
  • 第一列只能一直向下走,所以都是 1

解法一:暴力递归

原理

从终点反推:

  • (i,j) 的路径数 = 到 (i-1,j) + 到 (i,j-1)
  • 递归到边界结束

问题

会重复计算大量子问题(例如 (m-2,n-2) 会被多次计算),时间复杂度指数级,LeetCode 会超时。

Java 代码

java 复制代码
class Solution {
    public int uniquePaths(int m, int n) {
        return dfs(m - 1, n - 1);
    }

    private int dfs(int i, int j) {
        if (i < 0 || j < 0) return 0;
        if (i == 0 || j == 0) return 1;
        return dfs(i - 1, j) + dfs(i, j - 1);
    }
}

复杂度

  • 时间复杂度:O(2^(m+n))(近似指数级)
  • 空间复杂度:O(m+n)(递归栈深度)

解法二:记忆化搜索(递归 + 缓存)

原理

在暴力递归基础上,增加一个 memo[i][j]

  • memo[i][j] 已算过,直接返回
  • 否则递归计算并存储

避免重复子问题后,复杂度降到多项式级别。

时序图

dfs(i,j) Solution 调用方 dfs(i,j) Solution 调用方 uniquePaths(m,n) dfs(m-1,n-1) dfs(i-1,j) dfs(i,j-1) 写入 memoij 返回 memoij 返回结果

Java 代码

java 复制代码
class Solution {
    private int[][] memo;

    public int uniquePaths(int m, int n) {
        memo = new int[m][n];
        return dfs(m - 1, n - 1);
    }

    private int dfs(int i, int j) {
        if (i < 0 || j < 0) return 0;
        if (i == 0 || j == 0) return 1;
        if (memo[i][j] != 0) return memo[i][j];

        memo[i][j] = dfs(i - 1, j) + dfs(i, j - 1);
        return memo[i][j];
    }
}

复杂度

  • 时间复杂度:O(m*n)
  • 空间复杂度:O(m*n)(memo)+ O(m+n)(递归栈)

解法三:二维动态规划

原理

递归改迭代,按行或按列填表。

  1. 创建 dp[m][n]
  2. 初始化第一行、第一列为 1
  3. (1,1) 开始:
    dp[i][j] = dp[i-1][j] + dp[i][j-1]
  4. 返回 dp[m-1][n-1]

流程图



开始
创建 dpmn
第一行全部置 1
第一列全部置 1
双重循环 i=1..m-1, j=1..n-1
dpij=dpi-1j+dpij-1
遍历结束?
返回 dpm-1n-1
结束

Java 代码

java 复制代码
class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];

        for (int i = 0; i < m; i++) dp[i][0] = 1;
        for (int j = 0; j < n; j++) dp[0][j] = 1;

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }

        return dp[m - 1][n - 1];
    }
}

复杂度

  • 时间复杂度:O(m*n)
  • 空间复杂度:O(m*n)

解法四:一维动态规划(空间优化)

原理

观察二维 DP 转移式:

  • dp[i][j] 只依赖当前行左边值 dp[i][j-1]
  • 和上一行同列值 dp[i-1][j]

可以把二维压缩成一维数组 dp[j]

  • dp[j] 在更新前代表"上一行同列"
  • dp[j-1] 在更新后代表"当前行左侧"

转移:
d p j = d p j + d p j − 1 dpj = dpj + dpj-1 dpj=dpj+dpj−1

Java 代码

java 复制代码
class Solution {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n];

        // 第一行全是1
        for (int j = 0; j < n; j++) dp[j] = 1;

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[j] = dp[j] + dp[j - 1];
            }
        }

        return dp[n - 1];
    }
}

复杂度

  • 时间复杂度:O(m*n)
  • 空间复杂度:O(n)

解法五:数学组合

原理

从起点到终点总共要走:

  • 向下 m-1
  • 向右 n-1

总步数 m+n-2,本质是从这些步中选哪几步向下(或向右):

ans = C ( m + n − 2 , m − 1 ) = C ( m + n − 2 , n − 1 ) \text{ans} = C(m+n-2, m-1) = C(m+n-2, n-1) ans=C(m+n−2,m−1)=C(m+n−2,n−1)

为什么好

  • 不需要 DP 表
  • 复杂度最低(线性于较小维度)

Java 代码(避免中间溢出)

java 复制代码
class Solution {
    public int uniquePaths(int m, int n) {
        int total = m + n - 2;
        int k = Math.min(m - 1, n - 1);

        long ans = 1;
        for (int i = 1; i <= k; i++) {
            ans = ans * (total - k + i) / i;
        }
        return (int) ans;
    }
}

复杂度

  • 时间复杂度:O(min(m,n))
  • 空间复杂度:O(1)

各解法对比

解法 时间复杂度 空间复杂度 说明
暴力递归 指数级 O(m+n) 思路简单,但会超时
记忆化搜索 O(m*n) O(m*n) 递归写法友好
二维 DP O(m*n) O(m*n) 最经典,容易理解
一维 DP O(m*n) O(n) 工程上常用优化
数学组合 O(min(m,n)) O(1) 最简洁高效

推荐写法

面试/笔试建议优先:

  1. 二维 DP(解释最直观)
  2. 一维 DP(展示优化能力)
  3. 数学组合(展示抽象能力)
相关推荐
小欣加油5 小时前
leetcode56 合并区间
c++·算法·leetcode·职场和发展
lqqjuly5 小时前
前沿算法深度解析(二)
人工智能·算法·机器学习
徐小夕6 小时前
万字长文!千万级文档 RAG 知识库系统落地实践
前端·算法·github
akunkuntaimei6 小时前
2026年高考数学各省真题及答案(完整版)
算法·高考
Hello:CodeWorld7 小时前
C 风格变参 vs C++ 变参模板:核心区别与选型指南
c语言·c++·算法
8Qi88 小时前
LeetCode 516:最长回文子序列
算法·leetcode·职场和发展·动态规划
youngerwang9 小时前
【从搬运工到协处理器:网卡芯片架构、算法、验证与边缘演进深度剖析】
网络·算法·架构·芯片
KaMeidebaby10 小时前
卡梅德生物技术快报|纯化重组蛋白实操详解
人工智能·python·tcp/ip·算法·机器学习
手写码匠10 小时前
从零实现 Prompt 工程引擎:结构化提示、自动优化与多轮自省体系
人工智能·深度学习·算法·aigc
无限码力11 小时前
阿里算法岗 0530笔试真题 - 多约束条件下的元素匹配统计
算法·阿里笔试真题·阿里机试真题·阿里算法岗笔试