算法-跳跃游戏问题合集

1、跳跃游戏I

题目来源于leetcode-55
55. 跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

本题思路其实很简单,我们从后向前遍历,找到为0的元素,然后继续向前遍历,直到找到一个可以跳过0元素的位置,如果找不到,那么没法到达终点。

    public boolean canJump(int[] nums) {
        int curr=nums.length-2;
        while(curr>=0){
            while(nums[curr]!=0){//找到第一个0位置
                curr--;
                if(curr<0){
                    return true;
                }
            }
            int zeroIndex=curr;
            while(curr>=0&&nums[curr]+curr<=zeroIndex){
                curr--;
                if(curr<0){
                    return false;
                }
            }
            
        }
        return true;
    }

解法II。如果做了跳跃游戏II,我们可以得到另一种更加简单的解法:

    public boolean canJump(int[] nums) {
        int maxPos = 0;
        // 真正的跳跃点
        int jumpPoint = 0;
        for(int i = 0; i < nums.length - 1; i++) {
            maxPos = Math.max(maxPos, nums[i] + i);
            if(i == jumpPoint) {
                jumpPoint = maxPos;
            }
        }
        return jumpPonit >= nums.length - 1;
    }

2、跳跃游戏II

跳跃游戏II也是Leetcode上一道经典的题目,难度为hard,其实可以暴力求解,思路是使用两个指针,第一个指针指向初始位置,第二个指针指向i+nums[i]位置,在两个指针间的窗口中,选择一个nums[k]+k最大的值作为要跳跃的位置。时间复杂度是O(N),很符合我们的直观。

不过我们采用另一种O(N)的算法来解决这个问题

public int jump(int[] nums) {
	int maxPos=0;
	int bound=0;
	int miniStep=0;
	for(int i=0;i<nums.length-1;i++){
		//这里完成的就是暴力求解中的寻找跳跃点的操作了
		//不过我们不需要直接跳,我们只负责记录步数就行了
		maxPos=Math.max(maxPos,nums[i]+i);
		if(i==bound){
			bound=maxPos;
			miniStep++;
		}
	}
	return miniStep;
}

看到这里你可能会问,bound是啥?解释一下,bound就是跳跃点,显然0是第一个跳跃点,maxPos是在两个跳跃点之间的所有点能跳的最大位置,也就是下一个跳跃点,但是maxPos直到两个跳跃点内的节点全部遍历完才能知道。所以,当到达跳跃点的时候再更新下一个跳跃点位置,记录跳跃步数。

3、跳格子游戏(字节面试题)

input

3  5  0  7  1 4 0 2 1 1   3  2  0  0

int getMinSteps(int[] arr)


跳格子的游戏,每个格子上有一个数,从第一个格子出发,目标是到达最后一个格子,所在的格子上的数字,决定了下一步能够跳跃的最大距离。

找出最少的跳数

无法到达返回-1

尽量低的时间复杂度

本题是我面试字节的时候遇到的,与前两个跳跃游戏不同,本跳跃游戏是上面两个问题的结合体。有两种解决方式,第一种是利用跳跃游戏1判断能否跳到重点,然后用跳跃游戏2解决。第二种是在遍历的时候,我们判断他所能跳到最大位置是否可以超过终点。

在这里给出第二种解决方案。

    public static int jump(int[] nums){
        int bound=0;//真正的跳跃点
        int maxPos=0;//在每个位置上能跳的最大位置
        int step=0;
        for(int i=0;i<nums.length;i++){
            maxPos=Math.max(maxPos,i+nums[i]);
            if(i==bound){//到达跳跃点
                bound=maxPos;//边界是否可以到达
                step++;
            }
        }
        return bound<nums.length-1?-1:step;
    }

为什么判断bound可以用来表示是否能到达终点呢?以下面的序列为例:

3,2,0,2,1,1,0,2,1,1,3,2,0,0

可以看到,我们从第0个位置出发,在2110位置,我们无论如何是跳不过去的,此时,也就是bound指向了0的位置,在下一轮循环后,i已经指向bound后面的元素,因此,我们不再会遇到跳跃点,也就是说bound也不再更新,所以可以根据bound来判断能否到达终点。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值