指尖划过的轨迹,藏着最细腻的答案~
题目:
给你一个非负整数数组 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;}
};