当前位置: 首页 > news >正文

LeetCode算法日记 - Day 71: 不同路径、不同路径II

目录

1. 不同路径

1.1 题目解析

1.2 解法

1.3 代码实现

2. 不同路径

2.1 题目解析

2.2 解法

2.3 代码实现


1. 不同路径

https://leetcode.cn/problems/unique-paths/

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

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

问总共有多少条不同的路径?

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

1.1 题目解析

题目本质
这是一个典型的"路径计数"问题。机器人从起点 (1,1) 到终点 (m,n),每次只能向右或向下移动一步,求有多少种不同的走法。本质上是求"在约束条件下的方案数统计"。

常规解法
最直观的想法是用递归/DFS遍历所有可能路径:从起点开始,每个位置尝试向右和向下两个方向,递归到终点就算一条路径。

问题分析
纯递归会超时。假设 m=n=100,时间复杂度接近 O(2^(m+n)),存在大量重复计算。比如到达 (2,3) 这个位置的路径数会被反复计算。

思路转折
要想高效 → 必须避免重复计算 → 使用动态规划。
关键观察:到达某个格子 (i,j) 的路径数 = 到达其左边格子的路径数 + 到达其上边格子的路径数。因为机器人只能从左边或上边来。通过自底向上填表,每个状态只计算一次。

1.2 解法

算法思想

使用二维 DP,定义 dp[i][j] 表示从起点到达位置 (i,j) 的路径数。

递推公式:

dp[i][j] = dp[i-1][j] + dp[i][j-1]

边界条件:

  • dp[1][1] = 1(起点)

  • 第一行和第一列都只有一条路径

i)创建 (m+1) × (n+1) 的 dp 数组,使用 1-索引避免边界判断

ii)初始化起点 dp[1][1] = 1

iii)双层循环遍历所有格子,按递推公式累加

iv)返回 dp[m][n] 作为答案

易错点

  • 索引映射问题:使用 1-索引的 dp 数组时,起点是 dp[1][1] 而非 dp[0][0],这样可以自动处理边界(dp数组第0行第0列默认为0)

  • 重复初始化:在循环中不要重复设置 dp[1][1] = 1,在循环前设置好,防止重复初始化。

1.3 代码实现

static class Solution {int[][] dp;public int uniquePaths(int m, int n) {dp = new int[m + 1][n + 1];dp[1][1] = 1;  // 起点初始化for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {if (i == 1 && j == 1) {continue;  // 跳过起点}// 当前位置 = 左边来的 + 上边来的dp[i][j] = dp[i][j - 1] + dp[i - 1][j];}}return dp[m][n];}
}

复杂度分析

  • 时间复杂度:O(m × n),需要填充整个 dp 表格

  • 空间复杂度:O(m × n),使用了二维 dp 数组

2. 不同路径

https://leetcode.cn/problems/unique-paths-ii/description/

给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 109

示例 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

2.1 题目解析

题目本质
这是"不同路径"问题的升级版,增加了障碍物约束。本质上是"带约束条件的路径计数"问题:在网格中有障碍物的情况下,统计从左上角到右下角的所有可行路径数。

常规解法
最直观的想法是递归/DFS:从起点开始,遇到障碍物就回退,否则尝试向右和向下两个方向,递归到终点计数。

问题分析
纯递归会超时。对于 100×100 的网格,最坏情况时间复杂度接近 O(2^200),且存在大量重复子问题。比如计算到达 (5,5) 的路径数时,可能从多条不同路径重复计算。

思路转折
要想高效 → 必须消除重复计算 → 使用动态规划。
关键观察:如果当前格子是障碍物,路径数为 0;否则路径数 = 左边来的路径数 + 上边来的路径数。注意起点或终点是障碍物时,直接返回 0。

2.2 解法

算法思想

使用二维 DP,定义 dp[i][j] 表示从起点到达位置 (i,j) 的路径数。

递推公式:

if (grid[i-1][j-1] == 1):  // 当前是障碍物dp[i][j] = 0else:dp[i][j] = dp[i-1][j] + dp[i][j-1]

边界条件:

  • 起点是障碍物 → 直接返回 0

  • dp[1][1] = 1(起点可达时)

i)检查起点 grid[0][0],如果是障碍物直接返回 0

ii)创建 (m+1) × (n+1) 的 dp 数组,使用 1-索引

iii)初始化起点 dp[1][1] = 1

iv)双层循环遍历所有格子:

  • 如果是障碍物,dp[i][j] = 0

  • 否则累加左边和上边的路径数

v)返回 dp[m][n]

易错点

  • 起点障碍物判断:必须在循环前先判断 grid[0][0] == 1,否则会错误初始化

  • 索引映射混淆:dp 数组用 1-索引,但原数组是 0-索引。访问原数组时要用 num[i-1][j-1],容易写错

  • 障碍物处理时机:判断障碍物要在起点初始化之前,否则起点是障碍物时会错误赋值为 1

  • 终点障碍物:如果终点是障碍物,递推过程会自然得到 dp[m][n] = 0,无需额外判断

2.3 代码实现

static class Solution {int[][] dp;int m, n;public int uniquePathsWithObstacles(int[][] num) {m = num.length;n = num[0].length;dp = new int[m + 1][n + 1];// 起点是障碍物,直接返回if (num[0][0] == 1) return 0;dp[1][1] = 1;  // 起点初始化for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {if (i == 1 && j == 1) {continue;  // 跳过起点}if (num[i - 1][j - 1] == 1) {  // 当前格子是障碍物dp[i][j] = 0;              // 到不了,路径数是0} else {dp[i][j] = dp[i - 1][j] + dp[i][j - 1];  // 从上边和左边来}}}return dp[m][n];}
}

复杂度分析

  • 时间复杂度:O(m × n),需要遍历整个网格一次

  • 空间复杂度:O(m × n),使用了二维 dp 数组

http://www.dtcms.com/a/482355.html

相关文章:

  • 掌握string类:从基础到实战
  • 【C++】四阶龙格库塔算法实现递推轨道飞行器位置速度
  • 网站建设的费用怎么做账网站开发视频是存储的
  • 张店学校网站建设哪家好高端品牌衣服有哪些
  • 区域网站查询游戏代理平台
  • 分布式控制系统(DCS)的智能组网技术解析及解决方案
  • React18学习笔记(六) React中的类组件,极简的状态管理工具zustand,React中的Typescript
  • Jenkins 实现 Vue 项目自动化构建与远程服务器部署
  • Jenkins集成Jmeter压测实战攻略
  • Kubernetes 集群调度与PV和PVC
  • 工具: 下载vscode .vsix扩展文件方法
  • FastbuildAI后端ConsoleModule模块注册分析
  • Ubuntu安装Hbase
  • 恶意进程排查
  • Docker Desktop在MAC上无法强制关闭的命令清理方式
  • Android音频学习(二十二)——音频接口
  • 河北网站备案流程抖音代运营交1600押金
  • 专做正品 网站网站关键词优化培训
  • 2025年--Lc184--62.不同路径(动态规划)--Java版
  • 区块链的理解
  • 【GUI自动化测试】YAML 配置文件应用:从语法解析到 Python 读写
  • 使用llamaindex实现RAG时 的常见使用框架或工具
  • YOLOv3:高效精准的实时目标检测算法
  • 怎么做坑人网站同城新闻app有哪些
  • 网站建设需求调研问卷韶关网站开发
  • 设计模式篇之 责任链模式 Chain of Responsibility
  • 阿里云-ECS实例信息统计并发送统计报告到企业微信
  • LeetCode 刷题【119. 杨辉三角 II、120. 三角形最小路径和】
  • Jmeter跨线程组获取参数
  • Tomcat核心组件全解析