题干
给定一个长度为 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;而如果起点不能直接跳到终点,则需要先一跳跳到其他节点,再通过其他节点跳到终点。因此,我们可以维护一个滑动窗口,左端点指向当前节点,右端点指向在当前节点一跳可以到达的最远位置。遍历滑动窗口内的所有点,如果通过区间内某个点已经可以到达终点(参考LeetCode 55.跳跃游戏的思路,可以维护一个最大可达边界来判断是否可以达到终点。),那么我们就找到了最小跳数;如果遍历完区间内所有点,还不能达到终点,那么说明通过当前跳数不能到达终点,还需要跳更多下,因此将滑动窗口向右移动(左端点顶住上一个右端点,右端点指向当前最大可达边界),并将跳数+1,进入下一次循环。
// 滑动窗口
func jump(nums []int) int {
n := len(nums)
// 最大可达位置
var maxLoc int
// 滑动窗口的左右两端点
var left, right int
// 步数计数
var step int
// 循环终止条件:跳到终点
for maxLoc < n-1 {
// 移动滑动窗口
left = right
right = maxLoc
// 遍历滑动窗口中的所有节点,更新最大可达位置
for i := left; i <= right; i++ {
maxLoc = max(maxLoc, i+nums[i])
}
step++
}
return step
}
拿题目中的示例一来举例:
在该例子中,显然不符合起点就是终点的边界情况,即通过0跳就可以达到终点的情况。
第一次循环,检查是否可以只跳跃一次就达到终点,滑动窗口左右端点均指向起点,计算起点经过一跳的最大可达位置得到maxLoc为2,显然没有达到终点。
第二次循环,将滑动窗口向右侧平移,left为0,right为2(当前最大可达位置),检查是否可以经过两跳达到终点,遍历[2, 3, 1]计算最大可达位置,发现maxLoc为4,即通过3可以跳到终点,因此结束循环,得到最小跳数为2。