题目及测试
package pid045;
/*45. 跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
题目保证可以到达 nums[n-1]
*/
public class main {
public static void main(String[] args) {
int[][] testTable = {{2,3,1,1,4},{2,3,0,1,4},{1,2,3,5},{1,1,1,1}};
for (int[] ito : testTable) {
test(ito);
}
}
private static void test(int[] ito) {
Solution solution = new Solution();
int rtn;
long begin = System.currentTimeMillis();
for (int i = 0; i < ito.length; i++) {
System.out.print(ito[i]+" ");
}//开始时打印数组
rtn = solution.jump(ito);//执行程序
long end = System.currentTimeMillis();
System.out.println("rtn=" + rtn);
/*for (int i = 0; i < rtn; i++) {
System.out.print(ito[i]+" ");
}//打印结果几数组
*/ System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,366ms,极慢)
package pid045;
class Solution {
public int jump(int[] nums) {
int length = nums.length;
if(length<=2){
return length -1;
}
int[] result = new int[length];
result[0]=0;
result[1]=1;
for(int i=2;i<length;i++){
int min = Integer.MAX_VALUE;
for(int j=0;j<i;j++){
if(j+nums[j]>=i){
min = Math.min(min, result[j]+1);
}
}
result[i] = min;
}
return result[length-1];
}
}
解法2(别人的)
为了方便理解,我们将「起点」考虑进去。
由于起点是一定要经过的,因此,我们从起点开始跳,并初始化为第一步 step = 1,同时能得到从起点到达的最远距离,我们将其设置为边界 end,因为「我们不管起跳的方案是什么,我们只要每次贪心地能跳到最远即可」,因此,我们从左到右遍历每个能到达的地点,只要当前点没到边界 end,说明我们还在第一步的起跳范围内,我们就不起跳,步数还是原来的步数 1,同时,在到达第一步的边界 end 之前,我们就继续贪心地找到第二点到边界 end 之间能够到达的最远距离作为第二步的边界,用 nextMaxEnd 表示,表示未来第二步的边界(因为第二步一定是第二个点到边界 end 之间的某一个点,所以我们只能在这之间更新 nextMaxEnd);
遍历途中,一旦当前点来到了第一步的边界 end,说明第一步能够到达的最远距离已经走到了,必须跳第二步了 (注意这里并不是真正的起跳点,只是已经到达了第一步能到达的最远距离而已,无论如何你都必须跳出第二步了,我们只是贪心地每次都到最远距离了才肯跳出下一步,但一定要记住,这并不是真正的起跳方案),此时就把边界 end 更新为 nextMaxEnd 表示第二步的边界,并且 step++,表示你无论如何都得跳出第二步了,后面就是在下一个点到 end 之间更新第三步能够到达的最远距离即可,到达第二步的 end 时,继续更新 end 为 nextMaxEnd,step++;
以此类推,不断地在到达当前步的边界 end 前更新下一步能够到达的最远距离nextMaxEnd 作为下一步的边界,更新途中一旦到达当前步的边界 end,就表示当前步已经贪心地走完了,必须跳下一步了,step++,并且下一步能够到达的最远边界我们已经在之前比较出来了,直接更新即可:end = nextMaxEnd。
到这里,其实就可以明白为什么不用遍历终点了,因为边界 end 有可能是终点,那 end 一旦到了终点其实也就到了,但是我们的代码逻辑是到达 end 就得步数加一,其实这是没必要的。
下面的代码和官方的有一些不同,我是先初始化了第一步,比较好理解。
class Solution {
public int jump(int[] nums) {
if(nums.length == 1) return 0;
// 下面三行代码初始化第一步
int step = 1; // 第一步
int end = nums[0]; // 第一步的边界,也就是第一步能到达的最远距离
int nextMaxEnd = 0; // 下一步能到达的最远边界 (只要当前步到达它的边界 end,就更新为 nextMaxEnd 作为下一步的边界 end)
for(int i = 1; i < nums.length-1; i++) {
nextMaxEnd = Math.max(nextMaxEnd, nums[i]+i);
if(i == end) {
end = nextMaxEnd;
step++;
}
}
return step;
}
}