45. 跳跃游戏 II
题目:给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
链接 https://leetcode.cn/problems/jump-game-ii
个人思路
- 思路其实和前面跳跃游戏一样都是贪心算法,只是这次改为了求跳跃次数。详情参考https://blog.csdn.net/weixin_48127034/article/details/126268433
class Solution:
def jump(self, nums: List[int]) -> int:
if len(nums) == 1:
return 0
res = 0
length = len(nums)
cur = 0 # 当前所处位置
while cur < length:
jump = nums[cur]
if cur + jump + 1 >= length: # 说明接下来能一步结束
return res + 1
temp = nums[cur+1] + cur + 1
nextCur = cur+1
for j in range(cur+2,cur+jump+1):
if j + nums[j] >= temp:
temp = j + nums[j] # 更新能跳的最大距离
nextCur = j # 记录下一个位置
cur = nextCur
res += 1
return res
官方思路
(其实思路和官方的一样,只是每次都没有官方写的优雅简洁)
以下两种方法都是使用贪心算法实现,只是贪心的策略不同。
- 反向查找出发位置
我们的目标是到达数组的最后一个位置,因此我们可以考虑最后一步跳跃前所在的位置,该位置通过跳跃能够到达最后一个位置。
如果有多个位置通过跳跃都能够到达最后一个位置,那么我们应该如何进行选择呢?直观上来看,我们可以「贪心」地选择距离最后一个位置最远的那个位置,也就是对应下标最小的那个位置。因此,我们可以从左到右遍历数组,选择第一个满足要求的位置。
找到最后一步跳跃前所在的位置之后,我们继续贪心地寻找倒数第二步跳跃前所在的位置,以此类推,直到找到数组的开始位置。
使用这种方法编写的 C++ 和 Python 代码会超出时间限制,因此我们只给出 Java 和 Go 代码。
func jump(nums []int) int {
position := len(nums) - 1
steps := 0
for position > 0 {
for i := 0; i < position; i++ {
if i + nums[i] >= position {
position = i
steps++
break
}
}
}
return steps
}
2. 正向查找可到达的最大位置
在具体的实现中,我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。
在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。
class Solution:
def jump(self, nums: List[int]) -> int:
n = len(nums)
maxPos, end, step = 0, 0, 0
for i in range(n - 1):
if maxPos >= i:
maxPos = max(maxPos, i + nums[i])
if i == end:
end = maxPos
step += 1
return step
时间复杂度:O(n),其中 n 是数组长度。
空间复杂度:O(1)。
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/jump-game-ii/solution/tiao-yue-you-xi-ii-by-leetcode-solution/