0055. Jump Game (M)

Jump Game (M)

题目

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

题意

给定一个数组,每个数字元素代表以当前下标为起点,能够向右走的步数,判断该数组能否从第一个元素开始走到最后一个元素。

思路

使用回溯法会超时,原因在于对每个下标进行了多次判断。利用数组记录每个下标在递归过程中是否已经尝试过,以此对回溯法优化,可以AC,但运行时间将近1000ms。

动态规划:dp[i]代表以下标i结尾的左子数组整体能够走到的最远的下标。以 [3, 1, 2, 0] 为例,dp[0] = 3 表示子数组 [3] 最远能走到下标3,dp[1] = 3 表示子数组 [3, 1] 整体最远能走到下标3,dp[2] = 4 表示子数组 [3, 1, 2] 整体最远能走到下标4(虽然4已经在数组外)。不难发现规律:\(dp[i]=max(dp[i-1],\ i+nums[i])\),当然前提是 i <= dp[i - 1],即i必须是一个能够到达的点;如果不能到达i,则i之后所有的下标都无法到达,那么最后一个下标也一定无法到达。

官方解答(贪心):官方提供了一种更加高效省空间的贪心算法:用lastPos标记最左侧一个能够到达尾元素的下标,初始为nums.length - 1;从右向左扫描,如果当前下标i能够到达的最远位置 i + nums[i] >= lastPos,说明从i出发能够到达尾元素,更新 lastPos = i。最后判断 lastPos == 0。

迭代:可以直接用一个变量rightMax来标注最远能到达的位置,只要i小于等于rightMax,说明i位置是能到达的,不断更新rightMax,判断最后rightMax是否大于等于最后一个位置,实质上就是动态规划的简洁实现方式。


代码实现

Java

动态规划

class Solution {
    public boolean canJump(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            // 只有当i能到达才更新dp[i];i如果无法到达,则nums.length-1一定也无法到达
            if (i <= dp[i - 1]) {
                dp[i] = Math.max(dp[i - 1], i + nums[i]);
                // 如果判断已经可以到达最后一个下标,则提前结束
                if (dp[i] >= nums.length - 1) {
                    return true;
                }
            } else {
                return false;
            }
        }
        return true;
    }
}

贪心

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

回溯法

class Solution {
    boolean[] tried;

    public boolean canJump(int[] nums) {
        tried = new boolean[nums.length];
        return jump(nums, 0);
    }

    private boolean jump(int[] nums, int index) {
        if (index >= nums.length - 1) {
            return true;
        }

        for (int i = index + 1; i <= index + nums[index]; i++) {
            // 如果下标i已经尝试过,说明i走不通,无需再尝试
            if (tried[i]) {
                continue;
            }
            if (jump(nums, i)) {
                return true;
            }
            // 每次行不通则将i标记为已尝试
            tried[i] = true;
        }

        return false;
    }
}

JavaScript

迭代

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canJump = function (nums) {
  let rightMax = 0, i = 0
  while (i < nums.length && i <= rightMax) {
    rightMax = Math.max(rightMax, nums[i] + i)
    i++
  }
  return rightMax >= nums.length - 1
}
posted @ 2020-07-04 02:38  墨云黑  阅读(170)  评论(0编辑  收藏  举报