Skip to content
本页目录

0055-跳跃游戏

题目描述

https://leetcode.cn/problems/jump-game

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示: $$ 1 <= nums.length <= 3 * 10^4 \ 0 <= nums[i] <= 10^5 $$

思路1:递归倒推【超时】

  1. 就是先从最后一个数字开始,然后循环前面的数字,看是否能到达该步 nums[i] + i > targetIndex
  2. 如果能到达,就递归计算能到达的各个数字的前面是否能到达当前数
  3. 这个方法因为递归重复计算很多,容易超时
csharp
public class Solution {
    public bool CanJumpInternal(int[] nums, int targetIndex){
        if(targetIndex <0){
            return false;
        }
        if(targetIndex == 0){
            return true;
        }
        int startIndex = 0;
        int endIndex = targetIndex - 1;
        bool result = false;
        for(int i=startIndex;i<=endIndex;i++){
            result = result || (i + nums[i] >= targetIndex && CanJumpInternal(nums,i));
        }
        return result;
    }
    public bool CanJump(int[] nums) {
        return CanJumpInternal(nums,nums.Length-1);
    }
}

思路2:动规

思路二:动态规划 从第一步开始找出可能的最大值,如果超过了最后下标返回true

  1. 从第一步开始 dp 记录当前步可以到达的后面的最大值
  2. dp[0] = nums[0]
  3. dp[i] = max(dp[i-1]-1,nums[i])
  4. 状态转移方程的意思是,如果从前面一步走到当前步,那么前面一步的最大值就要减1,如果当前步的步数大于前面一步减一的值,那么就取当前步作为新的最大值。
  5. 需要注意边界处理,比如dp[i] = 0 的时候,如果当前就是最后一步,那么问题解决达成,如果当前不是最后一步,那么失败

注意边界测试用例[0,1] , [2,0,0]

csharp
public class Solution {
    
    public bool CanJump(int[] nums){
        int[] dp = new int[nums.Length];
        dp[0] = nums[0];
        //特殊边界处理 
        if(dp[0] == 0 && nums.Length > 1){
            return false;
        }
        for(int i=1;i<nums.Length;i++){
            dp[i] = Math.Max(dp[i-1]-1,nums[i]);
            if(dp[i] == 0 && nums.Length > i+1){//说明没到最后一步
                return false;
            }
        }
        return true;
    }
}

上述代码可以再简化一下:主要是初始化和边界处理合并

csharp
public class Solution {
    public bool CanJump(int[] nums){
        int[] dp = new int[nums.Length];
        for(int i=0;i<nums.Length;i++){
            dp[i] = i==0 ? nums[0] : Math.Max(dp[i-1]-1,nums[i]);
            if(dp[i] == 0 && nums.Length > i+1){//说明没到最后一步
                return false;
            }
        }
        return true;
    }
}

思路3:贪心算法

每一步,我们不要考虑跳一步还是跳几步,我们是查看当前位置可以调到最远的地方,然后记录为 cover,表示可以覆盖到最远的地方。 然后遍历当前的位置,直到 cover 位置,中间我们不停的记录新的 cover 值(取max),当 cover 可以覆盖到末尾时候,我们就知道可以达成目标,反之循环结束,说明不能跳到最后。

csharp
public class Solution {
    public bool CanJump(int[] nums){
        if(nums.Length == 1){ //直接一个元素,默认可以达到
            return true;
        }
        int cover = 0;
        for(int i=0; i<=cover; i++){ //注意这里是小于等于 cover , 从0跳1步,就是 <= 1
            cover = Math.Max(cover,nums[i]+i); 
            //Console.WriteLine("cover is {0}",cover);
            if(cover >= nums.Length-1){ //说明可以覆盖到终点,返回 true
                return true;
            }
        }
        return false;
    }
}

复习动规:20220514

csharp
public class Solution {
    public bool CanJump(int[] nums) {
        //动态规划 dp[i] 表示这一步能跳的最大距离
        int[] dp = new int[nums.Length];
        dp[0] = nums[0];
        //特殊边界处理 
        if(dp[0] == 0 && nums.Length > 1){
            return false;
        }
        for(int i=1; i<nums.Length; i++){
            dp[i] = Math.Max(dp[i-1]-1,nums[i]);
            if(dp[i] == 0 && nums.Length > i+1){ //不能跳了,但是还没有到最后一步
                return false;
            }
        }
        return true;
    }
}

复习贪心:20220514

csharp
public class Solution {
    public bool CanJump(int[] nums) {
        //从第一个数开始跳,选择后面能跳的最大的那个跳
        int index=0;
        int nextReach = nums[index]; //下一数的坐标
        while(index <= nextReach){
            nextReach = Math.Max(index + nums[index],nextReach); //循环取的可到达的最大位置
            if(nextReach >= nums.Length-1){
                return true;
            }
            index++;
        }
        return false || nums.Length == 1;
    }
}

复习:20220621

csharp

Released under the MIT License.