1.题目链接
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位 置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
◦ | 1 <= nums.length <= 10(4) | |
◦ |
| |
3. 解法:
和 跳跃游戏II 一样,仅需修改一下返回值即可。
Java算法代码:
class Solution {
public boolean canJump(int[] nums) {
int left = 0, right = 0, maxPos = 0, n = nums.length;
while(left <= right){
if(maxPos >= n - 1){
return true;
}
for(int i = left; i <= right; i++){
maxPos = Math.max(maxPos, nums[i] + i);
}
left = right + 1;
right = maxPos;
}
return false;
}
}
运行结果:
贪心策略:
新思路分析
a. 思路描述
- 观察:由于数组元素是非负整数(nums[i] >= 0),跳跃距离至少为 0。
- 关键点:
- 如果数组中没有 0,则每个位置都能跳跃至少 1 步(nums[i] >= 1),可以逐步前进到最后一个位置,返回 true。
- 如果数组中有 0,则 0 是一个“障碍”,因为到达 0 的位置后无法继续跳跃。
- 对于每个 0,判断是否能从 0 前面的某个位置跳到 0 后面的位置(即“越过” 0)。
- 对所有 0 进行判断,如果都能越过,则可以到达终点。
b. 思路可行性
- 正确性分析:
- 没有 0 的情况:如果 nums[i] >= 1 恒成立,则可以从索引 0 逐步跳到 n-1,每次至少跳 1 步。这种情况确实总是返回 true。
- 有 0 的情况:0 会阻断路径。例如 [3,2,1,0,4] 中,索引 3 的值为 0,到达后无法继续。需要检查是否能从索引 0、1 或 2 跳到索引 4(即 nums[i] + i >= 4)。
- 越过 0:对于每个 0 的位置 k,需要找到一个位置 i < k,使得 nums[i] + i > k。
- 所有 0 都可越过:如果每个 0 都能被越过,则路径不会中断,最终可以到达 n-1。
- 边界情况:
- 如果 n == 1,无需跳跃,直接可达。
- 如果最后一个位置是 0(nums[n-1] == 0),需要到达 n-1,无需继续跳跃。
- 结论:思路是可行的,且符合题目要求。
c. 与贪心策略的关系
- 思路实际上是贪心策略的一种变体:
- 贪心策略的核心是维护一个最远可达位置 maxPos,逐步扩展。
- 新方法关注“障碍”(0),通过检查是否能越过每个 0,隐式地实现了相同的覆盖检查。
- 两者的等价性:如果某个 0 无法越过,等价于贪心策略中的 maxPos 无法覆盖 n-1。
Java算法代码:
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
// 打印输入数组
System.out.println("Input array: " + Arrays.toString(nums));
// 边界情况:长度为 1,直接可达
if (n == 1) {
System.out.println("Array length is 1, directly reachable");
return true;
}
// 检查是否有 0
boolean hasZero = false;
for (int i = 0; i < n; i++) {
if (nums[i] == 0) {
hasZero = true;
break;
}
}
System.out.println("Has zero in array: " + hasZero);
// 如果没有 0,则一定可达
if (!hasZero) {
System.out.println("No zeros found, can reach the end");
return true;
}
// 检查每个 0 是否能被越过
for (int k = 0; k < n; k++) {
if (nums[k] != 0) continue; // 跳过非 0 位置
System.out.println("Found zero at position k=" + k);
// 如果 0 是最后一个位置,只需到达即可
if (k == n - 1) {
System.out.println("Zero is at the last position, check if reachable");
continue;
}
// 检查是否存在 i < k,使得 nums[i] + i > k
boolean canCross = false;
for (int i = 0; i < k; i++) {
int jumpDistance = nums[i] + i;
System.out.println(" Checking position i=" + i + ": nums[i]=" + nums[i] + ", can jump to " + jumpDistance);
if (jumpDistance > k) {
canCross = true;
System.out.println(" Can cross zero at k=" + k + " from i=" + i);
break;
}
}
// 如果无法越过这个 0,则无法到达
if (!canCross) {
System.out.println("Cannot cross zero at position k=" + k + ", cannot reach the end");
return false;
}
}
// 如果所有 0 都能越过,则可达
System.out.println("All zeros can be crossed, can reach the end");
return true;
}
}
分析:这里可以发现,这里就是分别进行判断(注释中该写的都写了)
长度为1,直接可以到达。和0无关。
如果没有0,可以到达,直接true。
如果有0,通过循环找零。从第一个找到的0开始处理。看前面的 i + nums[i]能不能越过0,如果能越过,继续找下一个。不能就false。不断循环。直到所有的0都能跳过,那么就是true'。
只要有一个不能就是false。
Input array: [3, 2, 1, 0, 4] Has zero in array: true Found zero at position k=3 Checking position i=0: nums[i]=3, can jump to 3 Checking position i=1: nums[i]=2, can jump to 3 Checking position i=2: nums[i]=1, can jump to 3 Cannot cross zero at position k=3, cannot reach the end