LeetCode 55. 跳跃游戏

题目描述

给定一个非负整数数组 nums,最初位于数组的第一个下标。nums[i] 表示从下标 i 出发最多可以向后跳多少步。

判断能否到达数组的最后一个下标。

例如:

输入:nums = [2,3,1,1,4]
输出:true

从下标 0 跳到下标 1,再从下标 1 跳到最后一个下标即可。

输入:nums = [3,2,1,0,4]
输出:false

无论怎样选择跳法,最远都只能到达下标 3,无法越过值为 0 的位置。

解题思路

这道题不需要记录具体的跳跃路径,只需要关心当前能够到达的最远下标。

定义:

int maxReach = 0;

maxReach 表示利用已经遍历过且能够到达的位置,最远可以到达哪个下标。

遍历数组时,对于当前位置 i,分成两种情况:

  • 如果 i > maxReach,说明当前位置已经超出当前可达范围。后面的下标更不可能到达,直接返回 false
  • 如果 i <= maxReach,说明当前位置可以到达,可以利用 nums[i] 继续扩展最远可达范围。

更新方式为:

maxReach = Math.max(maxReach, i + nums[i]);

这里保留原来的 maxReach,是因为当前位置提供的跳跃距离不一定能让可达范围变得更远。

为什么这是贪心

每遍历到一个可达位置,都选择保留所有已知方案中最远的可达下标。

我们不关心是经过哪些位置到达 maxReach 的,因为判断结果只取决于可达区间的右边界。只要下一个位置仍在这个区间内,就可以继续尝试扩大右边界。

因此,遍历过程中始终维护下面这个不变量:

[0, maxReach] 内的位置可由此前的可达位置覆盖

一旦出现 i > maxReach,可达区间与当前位置之间产生了断点,后续遍历也无法弥补这个断点。

手动模拟

nums = [2,3,1,1,4] 为例:

i = 0:maxReach = max(0, 0 + 2) = 2
i = 1:1 <= 2,可以到达;maxReach = max(2, 1 + 3) = 4
i = 2:2 <= 4,可以到达;maxReach 仍为 4
i = 3:3 <= 4,可以到达;maxReach 仍为 4
i = 4:4 <= 4,可以到达

遍历过程中没有出现不可达的位置,因此返回 true

再看 nums = [3,2,1,0,4]

i = 0:maxReach = 3
i = 1:maxReach = 3
i = 2:maxReach = 3
i = 3:maxReach = 3
i = 4:4 > 3,当前位置不可达

因此返回 false

代码实现

class Solution {
    public boolean canJump(int[] nums) {
        int maxReach = 0;
        for (int i = 0; i < nums.length; i++) {
            if (i > maxReach) return false;
            maxReach = Math.max(maxReach, i + nums[i]);
        }
        return true;
    }
}

易错点

1. 先更新,再判断当前位置是否可达

只有能够到达下标 i,才有资格使用 nums[i] 扩展可达范围。因此必须先判断:

if (i > maxReach) return false;

再更新 maxReach

2. 试图枚举每一种跳法

题目只要求判断能否到达终点,不要求输出具体路径。枚举所有跳法会产生大量重复状态,而维护最远可达下标就足以完成判断。

3. 把 nums[i] 当成能到达的下标

nums[i] 表示跳跃长度,从当前位置能够到达的最远下标应为:

i + nums[i]

复杂度分析

  • 时间复杂度:O(n)。数组中的每个位置最多遍历一次。
  • 空间复杂度:O(1)。只使用了 maxReach 和循环变量。

复盘

这道题的关键是把“选择下一次跳到哪里”转换为“当前最远能够覆盖到哪里”。

可以记住下面两步:

先检查:i 是否在当前可达范围内
再更新:maxReach = max(maxReach, i + nums[i])

贪心维护的是可达区间的最右端,不需要记录具体跳跃路径。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值