题解 | #几步可以从头跳到尾#
几步可以从头跳到尾
http://www.nowcoder.com/practice/de62bcee9f9a4881ac80cce6da42b135
题目
描述
- 给你一个长度为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; } }
- 时间复杂度:,一层循环;
- 空间复杂度:,常数级空间复杂度。
数据结构算法学习 文章被收录于专栏
个人算法学习,记录自己之前学过的东西,方便复习