45.跳跃游戏 II
题目描述
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入:nums = [2,3,1,1,4]
输出:2
解释:跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入:nums = [2,3,0,1,4]
输出:2
解题思路
思路一: 动态规划
我们用动态规划数组dp[i]记录到达数组第i个元素所走过的最小步数,遍历每一步走完所有可能。
假设我们当前到达第 i 个位置,下一步可以走到小于等于 nums[i] 值的所有地方,我们走 j 步到达 num[i + j],这里我们就可以退出状态转移方程式:dp[i + j] = Math.min(dp[i + j],dp[i] + 1);
实现代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
let len = nums.length, dp = Array(len).fill(len);
dp[0] = 0;
for (let i = 0; i < len; i++) {
for (let j = 1; j <= nums[i]; j++) {
if (i + j >= len) break;
dp[i + j] = Math.min(dp[i + j],dp[i] + 1);
}
}
return dp[len - 1];
};
-
时间复杂度:O(n^2),其中 n 是数组长度
-
空间复杂度:O(n)
优化版本, 参考https://leetcode.cn/problems/jump-game-ii/solution/tan-xin-shi-yao-de-wo-zhen-de-bu-hui-tan-a-zhi-nen/
不能判断 dp[i+1] 是否可以使用 dp[i] 的步数达到,可以再整个数组存可以到的最大步数
实现代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
let len = nums.length;
if(len == 1) return 0;
let step = Array(len).fill(0), dp = Array(len).fill(0);
// 第一步可以达到的最大位置
step[1] = nums[0];
for(let i = 1; i < len; ++i) {
// 假如前一个格子所花的步数能达到的最大位置 >= 当前位置,即前一个步数也可以到当前位置
dp[i] = step[dp[i-1]] >= i ? dp[i-1] : dp[i-1] + 1;
// 不能到达就再走一步
if(dp[i] + 1 < len) // 步数可能溢出,溢出说明一定可以在 n-1 步到达终点
// 这里计算的是从当前格子在走一步所能到达的最大距离
// 假如超过之前存储的最大步数,则更新
step[dp[i] + 1] = Math.max(step[dp[i] + 1], i + nums[i]);
}
return dp[len - 1];
};
-
时间复杂度:O(n),其中 n 是数组长度
-
空间复杂度:O(n)
思路二: 贪心算法
反向查找出发位置
我们可以「贪心」地选择距离最后一个位置最远的那个位置,也就是对应下标最小的那个位置。因此,我们可以从左到右遍历数组,选择第一个满足要求的位置。
找到最后一步跳跃前所在的位置之后,我们继续贪心地寻找倒数第二步跳跃前所在的位置,以此类推,直到找到数组的开始位置。
实现代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
if (nums.length == 1) return 0;
let position = nums.length - 1, steps = 0;
while (position > 0) {
for (let i = 0; i < position; i++) {
if (i + nums[i] >= position) {
position = i;
steps++;
break;
}
}
}
return steps;
};
-
时间复杂度:O(n^2),其中 n 是数组长度
-
空间复杂度:O(n)
反向查找出发位置
如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
if (nums.length == 1) return 0;
let end = 0, maxPosition = 0, steps = 0;
for(let i = 0; i < nums.length - 1; i++){
// 找能跳的最远的
maxPosition = Math.max(maxPosition, nums[i] + i);
if( i == end){ //遇到边界,就更新边界,并且步数加一
end = maxPosition;
steps++;
}
}
return steps;
};
-
时间复杂度:O(n),其中 n 是数组长度
-
空间复杂度:O(n)
参考资料
https://leetcode.cn/problems/jump-game-ii/solution/tan-xin-shi-yao-de-wo-zhen-de-bu-hui-tan-a-zhi-nen/
https://leetcode.cn/problems/jump-game-ii/solution/tiao-yue-you-xi-ii-by-leetcode-solution/