Jump Game (M)
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
jump length is 0, which makes it impossible to reach the last index.
题意
给定一个数组,每个数字元素代表以当前下标为起点,能够向右走的步数,判断该数组能否从第一个元素开始走到最后一个元素。
思路
使用回溯法会超时,原因在于对每个下标进行了多次判断。利用数组记录每个下标在递归过程中是否已经尝试过,以此对回溯法优化,可以AC,但运行时间将近1000ms。
动态规划:dp[i]代表以下标i结尾的左子数组整体能够走到的最远的下标。以 [3, 1, 2, 0] 为例,dp[0] = 3 表示子数组 [3] 最远能走到下标3,dp[1] = 3 表示子数组 [3, 1] 整体最远能走到下标3,dp[2] = 4 表示子数组 [3, 1, 2] 整体最远能走到下标4(虽然4已经在数组外)。不难发现规律:dp[i]=max(dp[i−1], i+nums[i])dp[i]=max(dp[i-1],\ i+nums[i])dp[i]=max(dp[i−1], i+nums[i]),当然前提是 i <= dp[i - 1],即i必须是一个能够到达的点;如果不能到达i,则i之后所有的下标都无法到达,那么最后一个下标也一定无法到达。
官方解答(贪心):官方提供了一种更加高效省空间的贪心算法:用lastPos标记最左侧一个能够到达尾元素的下标,初始为nums.length - 1;从右向左扫描,如果当前下标i能够到达的最远位置 i + nums[i] >= lastPos,说明从i出发能够到达尾元素,更新 lastPos = i。最后判断 lastPos == 0。
代码实现 - 动态规划
class Solution {
public boolean canJump(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
// 只有当i能到达才更新dp[i];i如果无法到达,则nums.length-1一定也无法到达
if (i <= dp[i - 1]) {
dp[i] = Math.max(dp[i - 1], i + nums[i]);
// 如果判断已经可以到达最后一个下标,则提前结束
if (dp[i] >= nums.length - 1) {
return true;
}
} else {
return false;
}
}
return true;
}
}
代码实现 - 官方解答(贪心)
class Solution {
public boolean canJump(int[] nums) {
int lastPos = nums.length - 1;
for (int i = nums.length - 2; i >= 0; i--) {
if (i + nums[i] >= lastPos) {
lastPos = i;
}
}
return lastPos == 0;
}
}
代码实现 - 回溯法
class Solution {
boolean[] tried;
public boolean canJump(int[] nums) {
tried = new boolean[nums.length];
return jump(nums, 0);
}
private boolean jump(int[] nums, int index) {
if (index >= nums.length - 1) {
return true;
}
for (int i = index + 1; i <= index + nums[index]; i++) {
// 如果下标i已经尝试过,说明i走不通,无需再尝试
if (tried[i]) {
continue;
}
if (jump(nums, i)) {
return true;
}
// 每次行不通则将i标记为已尝试
tried[i] = true;
}
return false;
}
}
本文探讨了Jump Game算法问题,旨在判断玩家是否能从游戏的起始位置到达终点。通过三种方法——回溯法、动态规划和贪心算法,详细解析了如何优化算法以提高效率。回溯法虽直观但效率低下,动态规划提供了更优解,而贪心算法则实现了时间和空间的双重优化。
406

被折叠的 条评论
为什么被折叠?



