算法——跳跃游戏问题

#贪心算法

跳跃游戏1——能否到达终点:

LeetCode55:

给你一个非负整数数组 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 , 所以永远不可能到达最后一个下标。

思路分析:

该问题中,我们只需要判断最终能否到达终点,不用思考每一步要具体跳跃到哪个位置,而是尽可能跳到最远的位置,看最远能够覆盖到哪里,只需要定义一个cover变量,来存放当前能覆盖到的最远位置,只要cover能够覆盖到末尾即可:

例如,在上图1中,3能够覆盖的范围是{2,1,0},2接下来能覆盖的是{1,0},1只能覆盖到{0},所以无法到达最终的4.

而在第二个数组中,2能覆盖{3,1},3可以覆盖{1,1,4},已经找到一条能够到达末尾的通路。1只能到下一个1,下一个1能够到达4,所以有{2,1,1,4},{2,3,1,1,4}两种走法,加起来有三种走法。

我们可以让变量cover表示能够到达的最远方位,将每次遍历的范围限制在i<=cover内,这样就能从前向后不断地扩大cover的范围,直到cover覆盖到终点,或i == cover时仍未到达终点。

cover可以这样进行更新:

cover = max(遍历的当前元素所能到达的最远位置,cover自身)

在上述例子中可以表示为:

  1. 在第二个图中,nums[0] = 2,此时cover = 2,能覆盖到{3,1};
  2. 继续,遍历到第二个元素(i=1),此时能覆盖的范围就是1+3,能覆盖{1,1,4}三个节点,此时的cover,对cover进行更新,cover=max(1+3,2),更新之后cover满足cover>=nums.length-1,遍历结束。

代码如下:

class Solution {
    public boolean canJump(int[] nums) {
        //如果长度为1,直接到达终点;
        if(nums.length == 1) return true;
        
        //定义cover来存储能到达的最远位置;
        int cover = 0;
        //数组的遍历范围需要限制在cover内,这样才能正确判断能否到达终点;
        for ( int i = 0 ;i<=cover; i++){
        //i+nums[i]表示当前节点能到达的最远位置,用它来和cover对比和更新;
            cover = Math.max(cover , i+ nums[i]);
            if(cover >= nums.length-1) 
                return true;
        }
        return false;
    }
}

跳跃游戏2——最短跳跃游戏:

LeetCode45:

给定一个长度为 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

解题思路:

此问题在上一题基础上要求最少跳跃次数,可以借助双指针和贪心算法来解决:

我们用left表示当前遍历的节点,在遍历时,首先记录该节点能到达的最远位置right,然后用一个变量steps来记录到达当前位置的最小步数,还需要一个临时变量cover,用来记录当前节点left能到达的最远位置,只有当left能够到达right的位置时,才能更新right和steps。

在图中,开始元素是2,首先让right到达从该点一步所能到达的最远位置nums[2],此时steps=1,也就是说只走一步最远只能到达nums[1],此时cover=nums[0]+0=2;

接下来需要开始讨论再走一步的情况,因为此时left和right中间的点都可以作为第二步的起点,所以我们继续遍历即可,当step=2时,此时可以作为起点的元素是{3,1},3能让我们到达left+nums[left] = 1 + 3 = 4,而1能让我们到达的最远位置是left+nums[left] = 2 + 1 = 3,所以我们将cover更新为4.

然后用left和right重新标记step=2能到达的区间:

此时还没有到达终点,需要继续走,现在left和right之间能作为起点的分别为{2,4},如果选择2可以到达left+nums[left] = 3 + 2 = 5,如果选择4则可以到达left+nums[left] = 4 + 4 =8,已经超越末尾了,此时step = 3,所以可以知道最少需要走三次才能到达终点。

Java代码实现如下:

class Solution {
    public int jump(int[] nums) {
        int right = 0;
        int steps = 0;
        int cover = 0;
        //left从头开始遍历;
        for(int left = 0; left < nums.length-1 ; left++){
        //cover记录当前steps下,能到达的最远位置;
            cover= Math.max(nums[left] + left ,cover);
        //当left == right时,表示已经计算完了该steps下,能走到的最远位置;
        //将right更新为cover,将最短步数steps++;
            if(left == right){
                right = cover;
                steps ++ ;
            }
        }
        if(right >= nums.length-1) 
        return steps;   
        return steps;    
    }
}

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值