这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
41. 不同路径 (unique-paths)
标签
- 动态规划
题目
这里不贴题了,leetcode打开就行,题目大意:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?注意这题是不能往左往上走的
相关知识
从动态规划这篇我们了解到动态规划的基本步骤是下面三步:
- 寻找最优子结构(状态表示)
- 归纳状态转移方程(状态计算)
- 边界初始化
基本步骤
接下来我们看下面具体问题
- 状态表示:
dp(i, j)表示从左上角走到(i, j)的路径数量,i,j范围分别是[0, m)和 [0, n)
- 状态转移方程:
- 由于我们每一步只能
向下或向右移动一步,那么其实只有从(i-1, j)或者(i, j-1)这两处走过来,那么转移方程也很简单dp(i, j) = dp(i-1, j) + dp(i, j-1)
- 边界初始化:
- 那么下面就是定边界,我们知道最上面一排和最左边一排,只有一种可能来的路径,
最顶时,来的路只有从左边1条,最左边来的路只有从上面1条。所以说,i = 0或j = 0时,都是边界条件可以把dp(0, j)和dp(i, 0)都至为1表示到达这个点路径可能条数是 1。 那么右下角坐标就是 (m-1, n-1)
1 1 1
1 0 0
1 0 0
写法实现
var uniquePaths = function(m, n) {
// 生成一个 m x n 的二维数组 dp
let dp = new Array(m).fill(0).map(item => new Array(n).fill(0))
// 初始化边界,dp(i, 0) 和 dp(0, j) 都至为 1
for (let i = 0; i < m; i++) {
dp[i][0] = 1
}
for (let j = 0; j < n; j++) {
dp[0][j] = 1
}
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j-1]
}
}
// (m-1, n-1)就是右下角
return dp[m-1][n-1]
};
let m = 3, n = 2
console.log(uniquePaths(m, n))
42. 不同路径 II (unique-paths-ii)
标签
- 动态规划
题目
这里不贴题了,leetcode打开就行,题目大意:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?注意这题是不能往左往上走的。
到这里都与上题一模一样,不同就是这次有障碍物。
基本步骤
总体思路和上题基本相同,动态规划。不同之处,障碍物的处理方法是 dp[i][j]=0
- 特判,注意起点就是障碍直接 return
- 还是使用边界条件初始化,注意障碍物的判断就好。第一行,只有左边一条,看上一个位置
dp[0][i-1]是否是0和看当前是否是障碍物obstacleGrid[0][i] === 1就是障碍物 - 循环遍历, 绕开障碍
obstacleGrid[i][j] === 1这些障碍。
写法实现
var uniquePathsWithObstacles = function(obstacleGrid) {
// 起点就是障碍物,直接没路径 return 0
if (obstacleGrid.length === 0 || obstacleGrid[0][0] === 1) {
return 0
}
let [m, n] = [obstacleGrid.length, obstacleGrid[0].length]
let dp = new Array(m).fill(0).map(item => new Array(n).fill(0))
// 初始边界,左上角为 1
dp[0][0] = 1
for (let i = 1; i < n; i++) {
// 第一行,只有左边一条,看上一个位置dp[0][i-1]是否是 0 和看当前是否是障碍物
if (dp[0][i-1] !== 0 && obstacleGrid[0][i] !== 1) {
dp[0][i] = 1
}
}
for (let j = 1; j < m; j++) {
// 第一列,只有上面来,看上一个位置 dp[j-1][0] 是否是 0 和看当前是否是障碍物 1
if (dp[j-1][0] !== 0 && obstacleGrid[j][0] !== 1) {
dp[j][0] = 1
}
}
// 再循环遍历, 并绕开障碍 obstacleGrid[i][j] !== 1
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (obstacleGrid[i][j] !== 1) {
dp[i][j] = dp[i - 1][j] + dp[i][j-1]
}
}
}
return dp[m-1][n-1]
};
let obstacleGrid = [[0,0,0],[1,0,0],[0,0,0]]
console.log(uniquePathsWithObstacles(obstacleGrid))
另外向大家着重推荐下这位大哥的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦
搜索我的微信号infinity_9368,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我
presious tower shock the rever monster,我看到就通过,暗号对不上不加哈,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧