给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4] 输出: true 解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4] 输出: false 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
看到这个题的第一反应,回溯???可想而知从位置零往后探索,后面的结果不能复用,解释一下,比如我在第一个点直接跳不到终点,需要借助后面的点的话,我要算第二个点能不能到,第三个点,第四个点。。。等我跳到第三个点,又要借助后面的点,又要算。。。一遍遍试探,时间要哭了。
顺着转变一下思路,我们可以从后往前试探,结果可以复用。解释一下,比如在计算倒数第三点能不能到终点的时候,如果需要借助倒数第二点来跳跃,因为我们是倒着来的,倒数第二点能不能到终点是已知的。
所以自然而然第一种解法:动态规划法
一个点能到终点的话只有两种情况,一、它本身的步数可以直接跳到终点;二、它可以跳到的后面的点中,有能跳到重点的点。
但是这运行时间,我真的要哭出声了
成功
执行用时 : 369 ms, 在Jump Game的Java提交中击败了9.05% 的用户
内存消耗 : 42.1 MB, 在Jump Game的Java提交中击败了69.06% 的用户
class Solution {
public boolean canJump(int[] nums) {
if(nums.length == 0)
return false;
//初始值默认为false
boolean[] dp = new boolean[nums.length];
//最后一个点跳最后一个点肯定可以到达
dp[nums.length-1] = true;
for(int i=nums.length-2; i>=0; i--){
//如果当前点的索引加上可以跳跃的最大值的结果大于了数组最后一位的索引值,可以到达
if((i+nums[i]) >= (nums.length-1) )
dp[i] = true;
//当前点为0,跳不动了
else if(nums[i] == 0)
continue;
else{
//当前点需要借助它后面的点(除了最后一个点)进行跳跃
//循环它能跳的过去的点
for(int j=i+1; j<=i+nums[i]; j++){
//能借助的这个点首先它能跳到终点
if(dp[j]){
dp[i] = true;
break;
}
}
}
}
return dp[0];
}
}
稍微优化一下?就是在找dp[i]的值的时候,当需要借助后面的点的时候,从能跳的最大值开始试探。
成功
执行用时 : 310 ms, 在Jump Game的Java提交中击败了10.71% 的用户
内存消耗 : 42 MB, 在Jump Game的Java提交中击败了69.47% 的用户
for(int j=i+nums[i]; j>i; j--){
//能借助的这个点首先它能跳到终点
if(dp[j]){
dp[i] = true;
break;
}
}
这样看来,不然贪心法?参考:https://www.cnblogs.com/grandyang/p/4371526.html
我们不关心它咋飞的,我们只关心它能飞多远。用一个max值存储,通过第一个点和目前正在遍历的点之间的所有点,我们能到达的最远距离。一旦可以到达终点,return.
成功
执行用时 : 4 ms, 在Jump Game的Java提交中击败了64.58% 的用户
内存消耗 : 41.4 MB, 在Jump Game的Java提交中击败了73.42% 的用户
class Solution {
public boolean canJump(int[] nums) {
if(nums.length == 0)
return false;
int max = 0;
for(int i=0; i<nums.length; i++){
//能到达的索引值已经小于当前的索引值,证明跳不过去了
if(max < i)
return false;
//能到达的最大的索引值大于最后一个值的索引,跳过去了
if(max >= nums.length-1)
return true;
//如果当前的点的索引加上它能跳的最大步数>当前能到达的最大索引值,更新max
max = Math.max(max, i+nums[i]);
}
return false;
}
}