leetcode:55. 跳跃游戏
题目
给定一个非负整数数组 nums
,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
解答 & 代码
解法一:普通动态规划
- 动态规划数组
**dp**
:dp[i]
代表下标为i
的位置能否到达(true
能到达,false
不能到达) - 状态转移方程:在下标
i
之前的位置(即下标区间为[0, i - 1]
),如果存在一个下标j
能够到达,且j + nums[j] >= i
,则下标i
也能到达,即dp[i] = true
;否则dp[i] = false
初始化:第一个下标能到达,即
dp[0] = true
class Solution {
public:
bool canJump(vector<int>& nums) {
int len = nums.size();
vector<bool> dp(len, false); // 动态规划数组,dp[i] 代表下标为 i 的位置能否到达
dp[0] = true; // 初始化
// 状态转移
for(int i = 1; i < len; ++i)
{
for(int j = i - 1; j >= 0; --j)
{
// 如果找到一个 j ∈ [0, i - 1],j 本身可到达,且 i 在 j 的跳跃范围之内
// 则 dp[i] = true,即下标 i 可到达
if(dp[j] == true && j + nums[j] >= i)
{
dp[i] = true;
break;
}
}
}
return dp[len - 1];
}
};
复杂度分析:设
nums
数组长度为 n时间复杂度
:双重循环
- 空间复杂度 O(n):dp 数组的空间复杂度为 O(n)
执行结果:
执行结果:通过
执行用时:132 ms, 在所有 C++ 提交中击败了 9.80% 的用户
内存消耗:47.5 MB, 在所有 C++ 提交中击败了 10.10% 的用户
解法二:贪心算法 动态规划
有关动态规划的问题,大多是让你求最值的,比如最长子序列,最小编辑距离,最长公共子串等等等。这就是规律,因为动态规划本身就是运筹学里的一种求最值的算法。 贪心算法作为特殊的动态规划也是一样,也一定是让你求个最值。 这道题表面上不是求最值,但是可以改一改:请问通过题目中的跳跃规则,最多能跳多远?如果能够越过最后一格,返回 true,否则返回 false。
贪心算法:每一步都计算一下从当前位置最远能够跳到哪里,然后和一个全局最优的最远位置 farthest
做对比,通过每一步的最优解,更新全局最优解,这就是贪心。
具体的,
设想一下,对于数组中的任意一个位置 y,我们如何判断它是否可以到达?根据题目的描述,只要存在一个位置 x,它本身可以到达,并且它跳跃的最大长度为 x + nums[x],这个值大于等于 y,即 x + nums[x] ≥ y,那么位置 y 也可以到达。
依次遍历数组 nums
中的每一个位置,设当前遍历到下标 i
:
- 如果当前位置在最远能跳到的位置
farthest
范围内,那么说明当前位置是可以到达的- 用当前位置能跳到的最远距离
i + nums[i]
更新全局可达最远位置farthest
- 如果最远可达位置
farthest
大于等于数组最有一个位置,那么说明数组最后一个位置可达,直接返回true
- 用当前位置能跳到的最远距离
否则,当前位置不可到达,直接返回
false
(后面的位置更不可能到达)class Solution {
public:
bool canJump(vector<int>& nums) {
int len = nums.size();
int farthest = 0; // 目前最远能跳到的位置(全局最优解)
// 遍历数组的每一个位置,并实时维护最远能跳到的位置 farthest
for(int i = 0; i < len; ++i)
{
// 如果当前位置在最远能跳到的位置 farthest 范围内,那么当前位置是可以到达的
if(i <= farthest)
{
// 用当前位置可达的最远位置 i + nums[i] 更新全局可达最远位置 farthest
farthest = max(farthest, i + nums[i]);
// 如果最远可达位置 farthest 大于等于数组最有一个位置,
// 那么说明数组最后一个位置可达,直接返回 true
if(farthest >= len - 1)
return true;
}
// 否则,当前位置不可到达,直接返回 false(后面的位置更不可能到达)
else
return false;
}
return false;
}
};
复杂度分析:设
nums
数组长度为 n时间复杂度 O(n):只遍历了一次数组
- 空间复杂度 O(1):
执行结果:
执行结果:通过
执行用时:56 ms, 在所有 C++ 提交中击败了 42.61% 的用户
内存消耗:47.2 MB, 在所有 C++ 提交中击败了 46.86% 的用户
换种写法:逻辑相同
class Solution {
public:
bool canJump(vector<int>& nums) {
int len = nums.size();
int farthest = 0;
for(int i = 0; i < len; ++i)
{
if(i > farthest)
return false;
farthest = max(farthest, i + nums[i]);
if(farthest >= len - 1)
return true;
}
return farthest >= len - 1;
}
};
执行结果:
执行结果:通过
执行用时:36 ms, 在所有 C++ 提交中击败了 98.31% 的用户
内存消耗:47.2 MB, 在所有 C++ 提交中击败了 68.24% 的用户