【题目】
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
提示:
1 <= nums.length <= 1040 <= nums[i] <= 1000- 题目保证可以到达
nums[n-1]
【题目解析】
解题思路
这个问题可以通过贪心算法高效地解决。核心思路是,在每一步跳跃中选择能够使得跳得最远的位置。具体实现时,我们维护当前能够到达的最远位置maxReach、当前步数能够到达的边界end,以及跳跃次数steps。
算法步骤
- 初始化:
maxReach初始化为0,表示最远能到达的位置;end初始化为0,表示当前步数能达到的最远边界;steps初始化为0,表示跳跃次数。 - 遍历数组:从第一个元素开始遍历,对于每个位置,更新
maxReach为当前位置加上跳跃长度和已知的maxReach之间的较大值。 - 更新边界和跳跃次数:当遍历到
end时,意味着达到了当前步骤能到达的最远边界,需要进行一次跳跃(增加steps),并将end更新为maxReach,表示下一次跳跃的最远边界。 - 达到数组末尾:当
end达到或超过数组末尾时,跳出循环,返回steps。
class Solution:
def jump(self, nums: List[int]) -> int:
maxReach, end, steps = 0, 0, 0
# 遍历数组,注意不需要访问最后一个元素,因为进入最后一个元素不需要跳跃
for i in range(len(nums) - 1):
# 更新最远能到达的位置
maxReach = max(maxReach, i + nums[i])
# 如果到达当前步骤的最远边界,进行跳跃,并更新边界
if i == end:
steps += 1
end = maxReach
return steps
执行用时
【总结】
适用问题类型
贪心算法适用于一类可以通过局部最优选择来达到全局最优解的问题。在这些问题中,每一步的最优决策可以基于当前状态和局部信息,而无需考虑解空间的其他可能性。跳跃游戏 II 正是这类问题的典型代表,其中需要找到到达数组末尾的最小跳跃次数。
解决算法:贪心算法
- 核心思想:在每一步跳跃中,总是选择能够到达的最远位置,以此来确保总跳跃次数最小。
- 实现策略:维护当前步骤能达到的最远位置
maxReach、当前步骤的跳跃边界end,以及总跳跃次数steps。通过更新这些变量,每次在边界处进行跳跃,直到到达数组末尾。
算法特点
- 高效率:贪心算法通过每次选择最佳决策,避免了复杂的回溯或全局搜索,保证了线性时间复杂度。
- 简单直观:算法逻辑简单,易于理解和实现,且无需额外的数据结构支持。
- 局限性:虽然贪心算法在某些问题上非常高效,但它并不适用于所有问题,特别是那些局部最优选择不能保证全局最优的问题。
算法优化与实践意义
- 优化方向:对于跳跃游戏 II,算法已经相当优化,难以进一步减少时间复杂度。不过,在具体实现中,可以通过简化逻辑或减少不必要的计算来进一步优化性能。
- 实践意义:贪心算法在解决资源分配、调度、网络流、压缩编码等问题中有广泛应用。跳跃游戏 II 的解法不仅提高了解决特定类型问题的能力,也加深了对贪心算法设计和实现的理解。掌握这种算法,有助于开发者在面对需要快速做出决策以达到最优解的问题时,设计出高效的解决方案。
总之,跳跃游戏 II 的贪心算法解法不仅展示了如何通过局部最优选择来实现全局最优解,也体现了贪心算法在算法设计中的重要性和实用性。通过这种方法,我们可以有效解决一系列优化问题,提高算法设计和问题解决的效率。