【leetCode】- 跳跃游戏

78 阅读2分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

55. 跳跃游戏

递归

这个问题很容易我们就容易想到递归,形式上我们考虑如下的问题:

  • 在第i阶我们可以选择走1-j步,这是我们递归的内容

  • 到第i阶,如果到达或者超过了最大的台阶数,就说明可以走到,这是我们的正确边界条件。

    • 至于走不到的情况?那就是1-j步里都走不到呗。

那根据这些推论,很容易就能构造出我们需要的递归方式的结果:

 public boolean canJump(int[] nums) {
     return t(nums,0);
 }
 ​
 public boolean t(int[] nums,int curF){
     if(curF>=nums.length-1) return true;
     for (int i = 1; i < nums[curF]; i++) {
         if(t(nums,curF+i)){
             return true;
         }
     }
     return false;
 }

结果: 超时

不出意料我们超时了,原因其实很简单:我们列举了过多的情况,实际上很多情况都重复了。

DP数组

既然超时了,那么当然我们考虑使用dp数组,来优化上面的问题。

我们注意到一个特性:

  • 在第j个台阶,是否能走到最后,是确定的。

因此我们可以使用布尔数组来记录结果,并且推知:

dp[i] = dp[i+1] | ... | dp[i+j] , j<=nums[i]

因此我们从后往前推,优化上面的算法如下:

 boolean[] dp = new boolean[nums.length];
 dp[nums.length-1] = true;
  for (int i = dp.length - 2; i >= 0; i--) {
      for (int i1 = 0; i1 <=nums[i] && !dp[i] ; i1++) {
          if(i+i1>nums.length-1){
              dp[i] = true;
              break;
          }
          dp[i] = dp[i+i1];
      }
  }
  return dp[0];

执行用时:209 ms, 在所有 Java 提交中击败了11.37%的用户

内存消耗:39.6 MB, 在所有 Java 提交中击败了78.73%的用户

贪心

从结果上看我们还是有些慢,原因跟上面相似:

  • 我们考虑了过多的结果,实际上我们只需要知道一个结果就可以了。

在这里考虑使用动态规划的特殊情况:贪心算法

我们知道:

  • 在第j台阶,最多走到j + nums[j]个台阶。

我们需要考虑的事情,其实是:

  • 从第0台阶开始,我们最多能走到哪个台阶?

那么其实我们只要记录在每次枚举范围内,我们最远可以走到哪里就可以了。

同时为了额外的情况,我们需要判断:如果当前枚举的位置已经超过了最远的距离,那么我们就不应该再往下而是退出。

 public static boolean canJump1(int[] nums) {
     int maxStep = 0;
     for (int i = 0; i < nums.length; i++) {
         if(i>maxStep) break;
         maxStep = Math.max(maxStep,i+nums[i]);
     }
     return maxStep>=nums.length-1;
 }

执行用时:3 ms, 在所有 Java 提交中击败了51.70%的用户

内存消耗:39.7 MB, 在所有 Java 提交中击败了69.69%的用户