力扣解题-55. 跳跃游戏

5 阅读5分钟

力扣解题-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;
  • inextIndex范围内,计算该位置能跳到的最远下标i + nums[i],并更新nextIndex为当前最大值;
  • nextIndex提前覆盖最后一个下标,可直接返回true(提前终止,优化性能)。
具体步骤
  1. 初始化核心变量
    • nextIndex = 0:初始最大可达下标为0(起点位置);
    • length = nums.length:数组长度,用于判断是否到达终点。
  2. 遍历数组更新最大可达范围
    • 遍历每个下标i(从0到length-1);
    • i > nextIndex(当前位置无法到达,后续位置更无法到达),直接返回false;
    • 计算当前位置能跳到的最远下标step = i + nums[i]
    • 更新nextIndexMath.max(nextIndex, step)(取当前最大可达范围);
    • step > length(提前覆盖终点,无需继续遍历),直接返回true;
  3. 遍历完成返回结果:若遍历结束未触发false,说明所有位置都能到达,返回true。
核心优化逻辑说明
  1. 时间复杂度最优:仅一次遍历数组(O(n)),每个元素仅被访问一次,且包含“提前终止”逻辑(覆盖终点时直接返回),耗时2ms击败94.82%用户;
  2. 空间复杂度极致:仅使用三个基础变量,空间复杂度O(1),无额外内存开销;
  3. 提前终止优化:当step > length时直接返回true,避免无效的后续遍历,进一步提升性能;
  4. 内存表现说明:内存消耗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(仅遍历能到达的位置),减少无效循环(但对时间复杂度无本质影响)。
具体步骤(优化版)
  1. 初始化变量maxReach = 0(最大可达下标)、n = nums.length
  2. 遍历可达范围:遍历i从0到n-1,且i <= maxReach(仅处理能到达的位置);
  3. 更新最大可达范围maxReach = Math.max(maxReach, i + nums[i])
  4. 提前终止判断:若maxReach >= n-1,直接返回true;
  5. 遍历结束返回false:若遍历完可达范围仍未覆盖终点,返回false。
核心优势说明
  1. 边界判断更精准:直接判断maxReach >= n-1(最后一个下标),避免step>length的逻辑误差;
  2. 性能更稳定:提前终止条件更贴合题目要求,在极端用例(如nums=[0]、nums=[1,0])下表现更优;
  3. 代码更简洁:逻辑聚焦“最大可达范围”,无冗余变量,是面试/工程中的最优写法。
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;
}

总结

  1. 第一次解答:核心逻辑正确,通过维护最大可达下标实现贪心判断,时间O(n)、空间O(1),性能优秀;
  2. 示例解答(优化版):优化边界判断条件,精准判断“覆盖最后一个下标”,是本题的最优写法,逻辑更严谨;
  3. 核心共性:两种解法均基于“贪心策略”,核心是不模拟跳跃路径,仅维护最大可达范围,避免动态规划等复杂思路的O(n)空间开销;
  4. 关键技巧:遍历过程中提前终止(覆盖终点/无法到达当前位置),是提升性能的核心优化点。