LeetCode 45.跳跃游戏II

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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值