题目

描述

  • 给你一个长度为n的数组A。
    A[i]表示从i这个位置开始最多能往后跳多少格。
    求从1开始最少需要跳几次就能到达第n个格子。

方法一

思路

  • 题目要求找出从1跳到n最快的路径,即所需步数最短,首先想到的就是遍历所有的路径,从中找出步数最短的路径,即为题目要求的从头跳到尾所需的步数。
  • 对于f(i,n)函数,i表示当前所处的下标,i从1开始,n表示尾格子,A[i]为最多可以从第i个格子往后跳几步,则存在如下的递推公式:
    图片说明
    图片说明

具体步骤

  • 1.判断所给数组是否为空,是否只有一个数,是则返回0,不是,执行步骤2;
  • 2.判断是否可以从1直接跳到n,是,返回1,不是,则执行步骤3;
  • 3.执行思路中的两个递推公式,求出最小的步数。
  • 代码如下:
    import java.util.*;
    public class Solution {
    /**
    * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
    *
    * 最少需要跳跃几次能跳到末尾
    * @param n int整型 数组A的长度
    * @param A int整型一维数组 数组A
    * @return int整型
    */
    public int Jump (int n, int[] A) {
       // write code here
       // 数组为空,或者长度为1,返回0
       if (A == null || A.length == 0 || A.length == 1){
           return 0;
       }
       // 能够从1开始直接跳到第n个格子
       if (1+A[0] >= n){
           return 1;
       }
       int res = 0;
       int[] dp = new int[n+1];
       // 假设从第n个格子到第n个格子所需步数为max,方便下面的循环运算
       dp[n] = Integer.MAX_VALUE;
       // 第n-1个格子到第n个格子的步数一定为1
       dp[n-1] = 1;
       int i = n - 2;
       while(i > 0){
           if (i + A[i-1] >= n){
               dp[i--] = 1;
               continue;
           }
           dp[i] = Integer.MAX_VALUE;
           // 实现递推公式
           for (int j = 1;j <= A[i-1]; ++j){
               dp[i] = dp[i] > dp[i+j] ? dp[i+j] : dp[i];
           }
           ++dp[i];
           --i;
       }
       return dp[1];
    }
    }
  • 时间复杂度:,最坏情况A[i] = n-i-1,这样程序在计算时,每次循环需要比较n-i-1次,1+2+3+4+……+n-1=n*(n-1)/2,即
  • 空间复杂度:,使用一维数组dp辅助运算。

方法二

思路

  • 方法一在运算过程中出现了运行超时的情况,无法完美解决问题,所以需要考虑对其进行优化,可以考虑使用贪心算法来降低时间复杂度。
  • 从n节点往前逆推,找出最远的能够到达n点的节点(所贪的就是这个距离),每次都找最远的,当到达0点时,其所消耗的步数自然是最小的。

具体步骤

  • 参考下图栗子:
    图片说明
  • 代码如下:
    import java.util.*;
    public class Solution {
    /**
    * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
    *
    * 最少需要跳跃几次能跳到末尾
    * @param n int整型 数组A的长度
    * @param A int整型一维数组 数组A
    * @return int整型
    */
    public int Jump (int n, int[] A) {
       // write code here
       int step = 0;
       // 数组为空,或者长度为1,返回0
       if (A == null || A.length == 0 || A.length == 1){
           return 0;
       }
       int reach = n-1;
       int index = 0;
       while(reach > 0){
           for (int i = 0; i < reach;++i){
               // 找到到达reach的最远点,更新
               if (i + A[i] >= reach){
                   reach = i;
                   ++step;
                   break;
               }
           }
       }
       return step;
    }
    }
  • 时间复杂度:,对于全1的数组,其退化到
  • 空间复杂度:,常数级空间复杂度。

方法三

思路

  • 方法二在全为1的情况下会退化至,因为在查找最远的能够到达n节点的点时,会找到第n-1个节点,再往下会找第n-2个节点……
  • 所以可以考虑从左往右逼近终点,让我们先不考虑究竟要怎么走才能以最少的步数到达终点。
  • 我们先从每一步来进行分析,首先假设我们现在处于indexj节点处,而当前的步长为A[indexj],所以我们可以一步走到indexj ~ indexj + A[indexj]这个范围内的任意一个节点,假设我们走到了index+i点,即令indexi = indexj + i,此时其下一步的范围就在indexi ~ indexi + A[indexi]之间了,我们同样可以一步走到这个范围内的任意节点,这整个过程就好比是一颗树形成过程,参考下图示例:
    图片说明
  • 那么现在我们有了一颗从0节点出发形成的m路树,可以看出,所求的最少步数也就是树的某一条从根节点到叶节点的最短路径。可以看见这棵树中有些冗余的部分,对其进行剪枝合并便得出下面的结果:
    图片说明
  • 当然图中的3,4节点的路径计算结果是可以共用的,这里为了美观,所以画了两次。实际图放出来看一看吧:
    图片说明
  • 也就是说我们不刻意的去找最少的步数的走法,而是一步一步往前走,每一步都尽量的使下一步能够到达更远的位置,当某一步能够到达的最远位置包括终点时,最短步数也就求出来了。
  • 故我们可以从下标0位置开始遍历数组A,每次都计算当前节点能够到达的最远节点,并与遍历到现在的能够到达的最远节点进行比较,保留最大值,由于每一步的步长是受所处节点限制的,所以需要记录当前所处节点index,那么index ~ index + A[index]这个范围便是一个步长所能遍历的范围,当超过这个范围时,需要更新index为目前统计的所能够到达的最远位置,并进行下一个步长的遍历以及记录步长,遍历结束,即为所统计步长即为最短步长。
  • 代码如下:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 最少需要跳跃几次能跳到末尾
* @param n int整型 数组A的长度
* @param A int整型一维数组 数组A
* @return int整型
*/
public int Jump (int n, int[] A) {

    // 步数
    int step = 0;
    // 数组为空,或者长度为1,返回0
    if (A == null || A.length == 0 || A.length == 1){
        return 0;
    }
    // 每走一步能够到达的最远位置
    int right = 0;
    // 当前这一步所处的位置
    int index = 0;
    // 遍历数组的指针
    int i = 0;
    int len = n-1;
    while(index < n && i < len){
        // 找出最远能够到达的位置
        right = Math.max(right,i + A[i]);
        // 遍历完了从index处能够走到的所有的位置
        // 走一步
        if (index == i){
            index = right;
            ++step;
        }
        ++i;
    }
    return step;
}
}
  • 时间复杂度:,一层循环;
  • 空间复杂度:,常数级空间复杂度。