LeetCode 45. Jump Game II
2020-05-20 09:39:31 # leetcode # core problems

Problem

LeetCode 45. Jump Game II

1. 题目简述

给出一个整形非负数组,初始在index为0的位置,每个位置上的数代表其所能跳跃的最大长度(不是只能跳跃该长度),保证一定能够跳到末尾节点,求从0节点跳到末尾节点的最少跳跃次数。例如:

Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
    Jump 1 step from index 0 to 1, then 3 steps to the last index.

2. 算法思路

Brute Force

首先,暴力求解法直接pass,我们需要遍历所有的步长,我们假设F(x)为从index为x的节点到末尾节点的最短jump数。暴力求解法伪代码:

1
2
3
4
5
6
7
F(x):
if x >= n - 1
return 0;
maxStep = nums[x];
for i from 1 to maxStep:
memo[x] = min(memo[x], F(x + i) + 1)
return memo[x]

Dynamic Programming

这里我们也可以用DP来减少重复计算,对于每个节点到末尾节点最少跳跃次数设为函数F(x),用memo数组来进行记录并更新。下面是top-down和bottom-up的两种算法。

DP1 top-down:

记录一个memo数组,长度为n,每个元素初始化为n(因为数组长度为n,最多跳跃次数也不过是n-1),计算F(0),对于每个0节点能到达的步长,进行比较,记录,找出最小的那个+1即为0节的最少跳跃数。

DP2 bottom-up:
记录一个memo数组,bottom-up算法就是要从base case开始进行计算,一直到目标target,本题中,base case是memo[n-1]=1,所以我们要从n-2开始往回遍历,判断n-2节点能否到达n-1节点,如果能,则memo[n-2]=1,不能则为0,一直遍历到memo[0],返回memo[0]。

两种DP算法时间复杂度均为O(n^2),空间复杂度为O(n)。

Greedy

本题是要找最少的跳跃步数,也就是说我们希望每一步都尽可能要跳得远,步子迈得大。举个例子:
[2,3,1,1,4],我们在0节点时,最大步长为2,因此最元能到达1和2两个节点,其最大步长分别为3和1,如果我们想走得远,我们当然希望能够第一次走到1节点,它的最大步长为3,可以到达4节点。

因此我们可以看出,对于初始节点0,它能够到达的最远为farthest为end,然后我们从0到end遍历找出其中x节点能跳到最远距离新的farthest,本次跳跃就跳到x节点,jump++,新的end为farthest,以此类推。直到最后,返回jump值。

3. 解法

  1. 暴力解法(Pass,上面有伪代码)
  2. DP top-down
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

class Solution{
public int jump(int[] nums) {
int n = nums.length;
int[] memo = new int[nums.length];
Arrays.fill(memo, n);
return minimumJump(nums, 0, memo);
}

private int minimumJump(int[] nums, int x, int[] memo) {
int n = nums.length;
if (x >= n - 1) {
return 0;
}
if (memo[x] < n) {
return memo[x];
}

int maxStep = nums[x];
for (int i = 1; i <= maxStep; i++) {
memo[x] = Math.min(memo[x], minimumJump(nums, x + i, memo) + 1);
}

return memo[x];
}
}

  1. DP bottom-up
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution{
public int jump(int[] nums) {
int n = nums.length;
int[] memo = new int[nums.length];
Arrays.fill(memo, n);
memo[n - 1] = 0;

for (int i = n - 2; i >= 0; i--) {
int maxStep = nums[i];
for (int j = 1; j <= maxStep; j++) {
if (i + j > n - 1) {
break;
}
memo[i] = Math.min(memo[i], memo[i + j] + 1);
}
}

return memo[0];
}
}
  1. Greedy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution{
public int jump(int[] nums) {
int currEnd = 0, farthest = 0, jump = 0;

for (int i = 0; i < nums.length - 1; i++) {
farthest = Math.max(farthest, i + nums[i]);
if (i == currEnd) {
currEnd = farthest;
jump++;
}
}

return jump;
}
}