力扣解题-55. 跳跃游戏
给你一个非负整数数组 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 <= 104
0 <= nums[i] <= 105
第一次解答
解题思路
核心方法:贪心策略 + 最大可达范围遍历,通过维护“当前能到达的最远下标”(nextIndex),单次遍历数组即可判断是否能到达最后一个下标,时间复杂度O(n)、空间复杂度O(1),是本题的高效解法。
核心原理铺垫
跳跃游戏的核心逻辑:能否到达终点,取决于遍历过程中能到达的最远下标是否覆盖最后一个位置。无需模拟具体的跳跃路径,只需实时更新“最大可达下标”:
nextIndex:记录遍历过程中能到达的最远下标,初始为0;- 遍历每个下标
i时,若i超出nextIndex,说明该位置无法到达,直接返回false; - 若
i在nextIndex范围内,计算该位置能跳到的最远下标i + nums[i],并更新nextIndex为当前最大值; - 若
nextIndex提前覆盖最后一个下标,可直接返回true(提前终止,优化性能)。
具体步骤
- 初始化核心变量:
nextIndex = 0:初始最大可达下标为0(起点位置);length = nums.length:数组长度,用于判断是否到达终点。
- 遍历数组更新最大可达范围:
- 遍历每个下标
i(从0到length-1); - 若
i > nextIndex(当前位置无法到达,后续位置更无法到达),直接返回false; - 计算当前位置能跳到的最远下标
step = i + nums[i]; - 更新
nextIndex为Math.max(nextIndex, step)(取当前最大可达范围); - 若
step > length(提前覆盖终点,无需继续遍历),直接返回true;
- 遍历每个下标
- 遍历完成返回结果:若遍历结束未触发false,说明所有位置都能到达,返回true。
核心优化逻辑说明
- 时间复杂度最优:仅一次遍历数组(O(n)),每个元素仅被访问一次,且包含“提前终止”逻辑(覆盖终点时直接返回),耗时2ms击败94.82%用户;
- 空间复杂度极致:仅使用三个基础变量,空间复杂度O(1),无额外内存开销;
- 提前终止优化:当
step > length时直接返回true,避免无效的后续遍历,进一步提升性能; - 内存表现说明:内存消耗47.31MB仅击败9.69%用户,是评测机环境差异导致,该解法的内存开销已达到理论最优。
public boolean canJump(int[] nums) {
int nextIndex=0;
int length=nums.length;
for(int i=0;i<length;i++){
if(i>nextIndex){
return false;
}
int step=i+nums[i];
nextIndex=Math.max(nextIndex,step);
if(step>length){
return true;
}
}
return true;
}
示例解答
解题思路
核心方法:贪心策略(优化版,提前终止+精准判断),在第一次解答的基础上优化终止条件,将“step>length”改为“nextIndex >= length-1”,避免数组长度判断的边界误差,是本题的最优写法,时间复杂度O(n)、空间复杂度O(1),性能更稳定。
核心原理升级
第一次解答中step>length的判断存在微小边界问题(如length=5时,最后一个下标是4,step>5虽能覆盖,但nextIndex >= length-1更精准)。优化后的核心逻辑:
- 遍历过程中,只要
nextIndex覆盖最后一个下标(length-1),就直接返回true; - 遍历范围可优化为
i <= nextIndex(仅遍历能到达的位置),减少无效循环(但对时间复杂度无本质影响)。
具体步骤(优化版)
- 初始化变量:
maxReach = 0(最大可达下标)、n = nums.length; - 遍历可达范围:遍历
i从0到n-1,且i <= maxReach(仅处理能到达的位置); - 更新最大可达范围:
maxReach = Math.max(maxReach, i + nums[i]); - 提前终止判断:若
maxReach >= n-1,直接返回true; - 遍历结束返回false:若遍历完可达范围仍未覆盖终点,返回false。
核心优势说明
- 边界判断更精准:直接判断
maxReach >= n-1(最后一个下标),避免step>length的逻辑误差; - 性能更稳定:提前终止条件更贴合题目要求,在极端用例(如nums=[0]、nums=[1,0])下表现更优;
- 代码更简洁:逻辑聚焦“最大可达范围”,无冗余变量,是面试/工程中的最优写法。
public boolean canJump(int[] nums) {
int maxReach = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
if (i > maxReach) {
return false;
}
maxReach = Math.max(maxReach, i + nums[i]);
if (maxReach >= n - 1) {
return true;
}
}
return false;
}
总结
- 第一次解答:核心逻辑正确,通过维护最大可达下标实现贪心判断,时间O(n)、空间O(1),性能优秀;
- 示例解答(优化版):优化边界判断条件,精准判断“覆盖最后一个下标”,是本题的最优写法,逻辑更严谨;
- 核心共性:两种解法均基于“贪心策略”,核心是不模拟跳跃路径,仅维护最大可达范围,避免动态规划等复杂思路的O(n)空间开销;
- 关键技巧:遍历过程中提前终止(覆盖终点/无法到达当前位置),是提升性能的核心优化点。