55. 跳跃游戏

33 篇文章 0 订阅
5 篇文章 0 订阅
给定一个非负整数数组,你最初位于数组的第一个位置。

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

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

根据leetcode官方给的题解可以知道这是一个动态规划的问题,一个动态规划问题需要以下四个步骤:
1.利用递归回溯解决问题
2.利用记忆表优化(自顶向下的动态规划)
3.移除递归的部分(自底向上的动态规划)
4.使用技巧减少时间和空间复杂度

方法1:回溯
这个方法低效,模拟从第一个位置跳到最后得所有方案。从第一个位置开始,模拟所有可以跳到的位置,然后再从当前位置重复上一个动作,当没有办法跳的时候,就回溯。
直觉上选择最大的步数去跳跃可以更快的到达终点

方法2:自顶向下的动态规划
可以理解为回溯法的优化,当确定一个坐标是好坐标或者坏坐标后,就不会改变了,这意味着我们可以记录这个结果,每次不用重复计算

方法3:自底向上的动态规划
这个方法有更好的时间效率,因为我们不需要栈空间,可以节省很多缓存开销。这让以后有更好的优化空间。回溯通常是通过反转动态规划的步骤来实现的

我们每次只会向右移动,意味着如果从右边开始动态规划,每次查询右边结点的信息,都是已经计算过的,不再需要额外的递归开销

enum Index {
    GOOD, BAD, UNKNOWN
}

public class Solution {
    public boolean canJump(int[] nums) {
        Index[] memo = new Index[nums.length];
        for (int i = 0; i < memo.length; i++) {
            memo[i] = Index.UNKNOWN;
        }
        memo[memo.length - 1] = Index.GOOD;

        for (int i = nums.length - 2; i >= 0; i--) {
            int furthestJump = Math.min(i + nums[i], nums.length - 1);
            for (int j = i + 1; j <= furthestJump; j++) {
                if (memo[j] == Index.GOOD) {
                    memo[i] = Index.GOOD;
                    break;
                }
            }
        }

        return memo[0] == Index.GOOD;
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/jump-game/solution/tiao-yue-you-xi-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法4:贪心
当把代码改成自底向上的模式,可以看出从某个位置出发,只需要找到第一个标记为good的坐标,也就是找到最左边的那个坐标。如果用一个单独的变量来记录最左边的good位置,一直重复到数组的开头,如果第一个坐标标记为good意味着可以从第一个位置跳到最后的位置

public class Solution {
    public boolean canJump(int[] nums) {
        int lastPos = nums.length - 1;
        for (int i = nums.length - 1; i >= 0; i--) {
            if (i + nums[i] >= lastPos) {
                lastPos = i;
            }
        }
        return lastPos == 0;
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/jump-game/solution/tiao-yue-you-xi-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

贪心最优解百分之百
下面也是在题解中看到,更通俗易懂

解法三
让我们直击问题的本质,与 45 题不同,我们并不需要知道最小的步数,所以我们对跳的过程并不感兴趣。并且如果数组里边没有 0,那么无论怎么跳,一定可以从第 0 个跳到最后一个位置。

所以我们只需要看 0 的位置,如果有 0 的话,我们只需要看 0 前边的位置,能不能跳过当前的 0 ,如果 0 前边的位置都不能跳过当前 0,那么直接返回 false。如果能的话,就看后边的 0 的情况。

public boolean canJump(int[] nums) {
    for (int i = 0; i < nums.length - 1; i++) {
        //找到 0 的位置
        if (nums[i] == 0) {
            int j = i - 1;
            boolean isCanSkipZero = false;
            while (j >= 0) {
                //判断 0 前边的元素能否跳过 0 
                if (j + nums[j] > i) {
                    isCanSkipZero = true;
                    break;
                }
                j--;
            }
            if (!isCanSkipZero) {
                return false;
            }
        }
    }
    return true;
}
但这样时间复杂度没有提高, 在 @Zhengwen 的提醒下,可以用下边的方法。

我们判断 0 前边的元素能否跳过 0 ,不需要每次都向前查找,我们只需要用一个变量保存当前能跳的最远的距离,然后判断最远距离和当前 0 的位置就可以了。

public boolean canJump(int[] nums) {
    int max = 0;
    for (int i = 0; i < nums.length - 1; i++) {
        if (nums[i] == 0 && i >= max) {
            return false;
        }
        max = Math.max(max, nums[i] + i);
    }
    return true;
}
时间复杂度:O(n)。

空间复杂度:O(1)。

我们甚至不需要考虑 0 的位置,只需要判断最大距离有没有超过当前的 i 。

public boolean canJump(int[] nums) {
    int max = 0; 
    for (int i = 0; i < nums.length; i++) {
        if (i > max) {
            return false;
        }
        max = Math.max(max, nums[i] + i);
    }
    return true;
}

作者:windliang
链接:https://leetcode-cn.com/problems/jump-game/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--17/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值