LeetCode 跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。

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

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

提示:

1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game

方法一:利用队列,道理和层序遍历时一样的
队列存放是从从队列中跳出的节点 index 可以到达的位置。所以每次跳出一个节点,只要判断index是否是数组最后一个下标即 index 是否等于 nums.length - 1即可,如果相等,说明可以到达,否则不可以到达。如果将整个队列遍历完之后,直接返回false,说明没有办法到达.

对应的代码:

class Solution {
    /*
    利用的是层序遍历的思想
    1、将当前位置可以到达的位置压入到队列中
    2、从队列中跳出一个节点,然后判断这个位置能不能到达终点,如果不能,那么判断它的值是否为0,如果为0,不做操作,
    否则就将它可以到达的位置再次压入到队列中
    3、当队列为空的时候,直接返回false
   */
    public boolean canJump(int[] nums) {
          int i,index,j,size;
          Queue<Integer> queue = new LinkedList<Integer>();
          boolean[] visited = new boolean[nums.length];
          queue.offer(0);
          while(!queue.isEmpty()){
              size = queue.size();
              for(i = 0; i < size; i++){
                index = queue.poll();
                if(index == nums.length - 1) //从队列中跳出一个元素,如果这个元素刚好是数组最后一个下标,直接返回true
                     return true;
                  /*
                   这一步可以不写,因为for循环中已经进行了判断,如果当前index对应的值是0,for循
                   环中j初始值是j = index + 1,那么会因为j > index + nums[index]而不会执行for循
                   环中的内容 
                if(nums[index] == 0) 
                     continue;
                 */
                for(j = index + 1; j < nums.length && j <= index + nums[index]; j++){
                /*
                注意为了避免超时,需要定义一个数组,标记j这个位置是否已经压入到队列了,如果是,那么
                就不要再次压入到队列中了,从而使得队列的长度减少,进而降低时间复杂度。
                例如[3,2,1,1],当index = 0的时候,就已经将2,1,1对应的下标1,2,3压入到队列中了
                如果没有这个if判断,直接将j压入,那么导致下一次while循环中,当跳出2的时候,还需将
                1,1对应的2,3下标压入到队列中,从而增加了时间复杂度。

                同样的,为了标记j这个位置是否已经被访问了,不可以再将j位置压入到队列之后,就将它的
                值更新成为一个负数,尽管这个题目中的所有元素都是非负数,因为再这个for循环中需要用
                到j对应的值,一旦在将j位置压入到队列的时候,那么当从队列中跳出的下标index为j的时候
                那么index对应的值就变成了一个负数,从而就没有办法获取index可以到达的位置了,所以
                这里是重新定义一个数组来标记是否已经被访问过了
                */
                    if(!visited[j]){
                        queue.offer(j);
                        visited[j] = true;
                    }
                }
            }
          }
          return false;
    }

}

运行结果:
在这里插入图片描述

方法二:贪心算法

class Solution {
    public boolean canJump(int[] nums) {
          int i,j,rightMost;
          rightMost = 0;//表示i下标能够到达的最远的位置下标
          for(i = 0; i <= rightMost && i < nums.length; i++){
               /*
                再当前的元素没有超过能到到的最右边的位置的时候,那么就更新rightMost,如果更新之后
                的rightMost大于等于nums.length - 1,直接返回true,否则继续更新,一旦超过了
                rightMost,直接返回false
               */  
                rightMost = Math.max(rightMost,i + nums[i]);
                if(rightMost >= nums.length - 1)
                   return true;
          }
          return false;
    }
}

运行结果:
在这里插入图片描述

LeetCode 跳跃游戏II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。假设你总是可以到达数组的最后一个位置。

示例 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-ii

方法一:利用队列
对应的代码:

class Solution {
    public int jump(int[] nums) {
       Queue<Integer> queue = new LinkedList<Integer>();
       boolean[] visited = new boolean[nums.length];
       queue.offer(0);
       visited[0] = true;
       int i,index,j,size;
       int count = 0;
       while(!queue.isEmpty()){
       /*
       队列存放的是跳跃第count次的时候到达的所有可能位置,所以一旦从队列中跳出的值刚好是最后一个
       元素的下标,就可以直接返回count,否则,就将当前跳出的值可以到达的位置压入到队列中。当for循环
       结束之后才执行count + 1,这样才可以保证队列中的元素才是跳跃第count + 1次的所有可能位置
       */
           size = queue.size();
           for(i = 0; i < size; i++){
                index = queue.poll();
                if(index == nums.length - 1)
                   return count;
                 /*
                 可以不写
                if(nums[index] == 0)//如果当前下标对应的值是0,那么直接进行下一次循环
                  continue;
                */
                for(j = index + 1; j < nums.length && j <= index + nums[index]; j++){
                   if(!visited[j]){
                      /*
                       注意不可以将这个判断改成!visited[j] && nums[j] != 0
                      因为有可能最后一个元素是0,那么这时候如果if判断是加上了nums[j] != 0,那么就
                      会导致最后一个元素没有添加到队列中,从而导致错误
                      */ 
                     queue.offer(j);
                     visited[j] = true;
                   }
                }
           }
           count++;//在for循环结束之后才执行,从而将跳跃第count + 1的所有可能压入到队列中
       }
       return -1;
    }
}

运行结果:
在这里插入图片描述

方法二:贪心
在理解上面的方法之后,在看这个代码会更加容易理解。
其中if(i == end)这个判断相当于上面的代码中for循环结束之后进行count++.

class Solution {
    public int jump(int[] nums){
        int count = 0,i,farthest = 0,end = 0;
        for(i = 0; i < nums.length - 1;i++){
           farthest = Math.max(farthest,nums[i] + i);//获取当前下标能够到达的最远位置
           if(i == end){
               //更新最远farthest的时候,count++
               end = farthest;
               count++;
           }
        }
        return count;
    }

}

运行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值