LeetCode 45. 跳跃游戏 II【中等】

53 阅读3分钟

题干

给定一个长度为 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跳就可以达到终点的情况。
第一次循环,检查是否可以只跳跃一次就达到终点,滑动窗口左右端点均指向起点,计算起点经过一跳的最大可达位置得到maxLoc2,显然没有达到终点。
第二次循环,将滑动窗口向右侧平移,left0right2(当前最大可达位置),检查是否可以经过两跳达到终点,遍历[2, 3, 1]计算最大可达位置,发现maxLoc4,即通过3可以跳到终点,因此结束循环,得到最小跳数为2

算法流程.png