LeetCode 45. 跳跃游戏 II
题目说明
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例 :
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
来源:力扣(LeetCode)
题目链接
难度:困难
本题参考了官方题解的思路。
方法一:贪心算法(双向遍历)
第一个指针index指向最后一个节点,然后利用贪心的思路,每次从左向右寻找离它最远的可达点。然后index设置为该点,继续重复操作。以此类推,直到index指向数组开始位置。
那么为什么要用离它最远的可达点?
我们可以假设对于num[index]点,在它左边存在n个点可以一步到达num[index],其中离它最远的点取名为num[far],最近的点取名为num[near]。
那么对于这n个点来说,在num[far - i]的集合中,能一步到达num[near]的点,一定也能一步到达其他n - 1个点。反之能一步到达num[far]的点,却不一定能一步到达其他n - 1个点。
如果我们给到达这n个点之前走过的步数取名为steps的话,那么steps[far] 一定小于等于其他的steps。所以要取最远的可达点。
时间复杂度:最佳O(n),最差O(n^2)
class Solution {
public int jump(int[] nums) {
int index = nums.length - 1;
int min = 0;
while(index > 0){
int i = 0;
for(; i < index; i++){
if(nums[i] >= index - i){
break;
}
}
min ++;
index = i;
}
return min;
}
}
方法二:贪心算法(正序遍历一次)
可以正序遍历一次求解这个问题。
在遍历的过程中,计算 i 到 next之间所有点自身的最远可达距离中的最大值,作为下一段遍历区间的结束位置next,每段遍历结束时步数 + 1。直到遍历到数组尽头。
为什么选取 i 到 next之间所有点最远可达距离中的最大值?
个人比较通俗的理解:贪心贪的是i+nums[i],每次选择i 到 next之间i+nums[i]最大的,也就是选择了一个相同步数下接下来范围最大的解,涵盖了其他点的选择范围。就算nums[maxPos]这个点的值是0, 我把这个最大范围减1也大于等于其他的可能范围。即从其它点出发能到达的范围,如果超过了最佳点的索引值,在相同的步数下这个最佳点一定也能到达;如果没超过最佳点的索引值,那还要额外的步数才能到最佳点呢。
时间复杂度:O(n)
class Solution {
public int jump(int[] nums) {
int maxPos = 0;
int i = 0;
int steps = 0;
int next = 0;
while(next < nums.length - 1){
maxPos = Math.max(maxPos, i + nums[i]);
if(next == i){
next = maxPos;
steps ++;
}
i ++;
}
return steps;
}
}