1. 跳跃游戏问题解析
今天我们来拆解Leetcode第55题"跳跃游戏",这是一个经典的贪心算法问题。题目描述很简单:给定一个非负整数数组nums,你最初位于数组的第一个位置,数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
这个问题看似简单,但蕴含着几个关键点需要理解:
- 数组索引代表当前位置
- 数组值代表从该位置能跳跃的最大步数
- 目标是判断能否从起点跳到终点
1.1 问题示例分析
举个例子,给定数组[2,3,1,1,4]:
- 位置0(值为2)可以跳到位置1或2
- 位置1(值为3)可以跳到位置2、3或4
- 位置4就是终点
这个例子中,我们可以通过0→1→4的跳跃路径到达终点,所以返回true。
再看反例[3,2,1,0,4]:
- 从位置0最多可以跳到位置3
- 但位置3的值为0,无法继续跳跃
- 无法到达终点,返回false
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路与算法选择
2.1 暴力解法与优化
最直观的想法是使用回溯法,尝试所有可能的跳跃路径。但这种时间复杂度是指数级的,对于大数组会非常低效。
更好的方法是使用贪心算法。贪心算法的核心思想是:在每一步选择中都采取当前状态下最优的选择,从而希望导致全局最优的结果。
2.2 贪心算法设计
我们可以维护一个变量maxReach,表示当前能够到达的最远位置。遍历数组时:
- 如果当前位置i超过了maxReach,说明无法到达i,自然也到不了终点
- 否则,更新maxReach为max(maxReach, i + nums[i])
- 如果maxReach已经≥最后一个位置,提前返回true
这种解法的时间复杂度是O(n),空间复杂度是O(1),非常高效。
3. JavaScript实现详解
3.1 基础实现代码
javascript复制function canJump(nums) {
let maxReach = 0;
for (let i = 0; i < nums.length; i++) {
if (i > maxReach) return false;
maxReach = Math.max(maxReach, i + nums[i]);
if (maxReach >= nums.length - 1) return true;
}
return true;
}
3.2 代码逐行解析
- 初始化maxReach为0,表示初始时能到达的最远位置是起点
- 遍历数组:
- 检查当前位置i是否超过了maxReach
- 更新maxReach为当前位置能到达的最远位置
- 如果已经能到达终点,提前返回
- 如果遍历结束都没返回false,说明可以到达终点
3.3 边界情况处理
需要考虑的特殊情况:
- 空数组:题目保证非空,可不处理
- 单元素数组:直接返回true
- 包含0的数组:需要确保能跳过这些0
4. 算法优化与变种
4.1 反向贪心解法
另一种思路是从终点往前推:
javascript复制function canJump(nums) {
let lastPos = nums.length - 1;
for (let i = nums.length - 2; i >= 0; i--) {
if (i + nums[i] >= lastPos) {
lastPos = i;
}
}
return lastPos === 0;
}
这种解法同样O(n)时间复杂度,但实际运行可能稍慢,因为总是要遍历整个数组。
4.2 跳跃游戏II变种
Leetcode第45题是跳跃游戏II,要求找出到达终点的最少跳跃次数。解法类似但需要记录当前步数能到达的范围和下一步能到达的最远位置。
5. 常见错误与调试技巧
5.1 新手常见错误
- 忘记处理i > maxReach的情况
- 错误计算maxReach(如写成maxReach = nums[i])
- 循环终止条件错误(如i <= nums.length)
5.2 调试建议
对于难以理解的case,可以:
- 打印每次循环后的maxReach值
- 在小数组上手动模拟执行
- 使用Leetcode的测试用例调试功能
6. 实际应用场景
跳跃游戏问题虽然抽象,但可以建模很多实际问题:
- 网络路由选择
- 游戏角色移动路径规划
- 资源分配问题
- 股票买卖时机选择
理解这类问题的解法有助于培养贪心算法的思维模式,这在解决优化问题时非常有用。
7. 性能分析与优化
7.1 时间复杂度对比
- 回溯法:O(2^n)
- 动态规划:O(n^2)
- 贪心算法:O(n)
贪心算法明显最优,特别是对于大数组。
7.2 实际测试数据
在Leetcode上测试:
- 贪心算法:80-100ms
- 动态规划:超时
- 回溯法:超时
8. 扩展练习建议
为了巩固这个算法:
- 尝试解决跳跃游戏II(Leetcode 45)
- 尝试用动态规划实现
- 修改问题为输出所有可能的跳跃路径
- 尝试在其他编程语言中实现
9. 个人解题心得
在实际解决这个问题时,我最初尝试了动态规划方法,但发现效率不够。后来通过分析问题特性,发现贪心算法更合适。关键是要理解:我们不需要知道具体的跳跃路径,只需要知道能否到达终点。这种"只关心最远能到哪"的思路是贪心算法的典型应用。
另一个心得是:对于数组遍历问题,考虑从前往后和从后往前两种方向,有时会有不同的启发。比如这个问题的两种贪心解法就是从不同方向思考的结果。
