指尖划过的轨迹,藏着最细腻的答案~

题目:

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

示例 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 <= 10^4
  • 0 <= nums[i] <= 10^5

分析:

根据题意,我们需要在遍历到下标 i 时判断前面的跳跃限制是否可以到达当前位置i,需要使用前面的信息,可以考虑使用 动态规划,而动态规划有三步,分别是:

  • 明确dp数组含义: 本题我们定义dp[i]为下标i可以达到的最远的下标,例如: dp[2] = 3,即在遍历到nums为i时最远可以跳跃到下标3的位置。
    我们为什么要这么定义呢?这是因为题目只是要求判断是否可以跳跃到最后一个位置,我们每次都贪心的尽可能的到达最远的位置,这样答案一定会被包含在最终的结果中。

  • 推导公式: 对于当前下标 i 来说其最远的跳跃位置由两个方式决定,分别是前一个下标i-1的最远跳跃位置dp[i-1]与当前下标可以跳跃的最大距离i + nums[i],取二者中的较大值,最终公式如下: $$ dp[i] = min(dp[i-1], i + nums[i]) $$

  • 初始化: 根据上面公式,在i为0时,会出现堆栈溢出,所以我们需要先将 i 为0时初始化,dp[0] = nums[0];

AC代码:

方法一:贪心 + 动态规划 空间复杂度O(n) 见分析。

class Solution {
public:bool canJump(vector<int>& nums) {int n = nums.size();if (nums[0] == 0 && n > 1) return false;vector<int> dp(n, 0);dp[0] = nums[0];for (int i = 1; i < n; i++) {dp[i] = max(i + nums[i], dp[i-1]);if (dp[i] <= i && i != n - 1) {return false;}}return true;}
};

方法二:贪心 + 动态规划 空间复杂度O(1)

根据dp的递推公式: $$ dp[i] = min(dp[i-1], i + nums[i]) $$ 我们可以知道,dp[i]只与dp[i-1]有关,因此我们完全可是使用一个变量prev来记录 dp[i-1] 的值,如下:

class Solution {
public:bool canJump(vector<int>& nums) {int n = nums.size();if (nums[0] == 0 && n > 1) return false;int prev = nums[0]; // i- 1能达到的最远的位置for (int i = 1; i < n; i++) {int curMax = max(i + nums[i], prev);if (curMax <= i && i != n - 1) {return false;}prev = curMax;}return true;}
};