算法-跳跃游戏
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来判断能否到达终点。