题目描述

image.png

题目链接

https://leetcode.cn/problems/jump-game-ii/

思路

这道题是典型的贪心算法,通过局部最优解得到全局最优解。我们可以「贪心」地进行正向查找,每次找到可到达的最远位置,这样就可以在线性时间内得到最少的跳跃次数。

例如,对于数组【45】跳跃游戏Ⅱ - 图2,初始位置是下标【45】跳跃游戏Ⅱ - 图3,从下标【45】跳跃游戏Ⅱ - 图4出发,最远可到达下标【45】跳跃游戏Ⅱ - 图5。下标【45】跳跃游戏Ⅱ - 图6可到达的位置中,下标【45】跳跃游戏Ⅱ - 图7的值是【45】跳跃游戏Ⅱ - 图8,从下标【45】跳跃游戏Ⅱ - 图9出发可以达到更远的位置,因此第一步到达下标【45】跳跃游戏Ⅱ - 图10。从下标【45】跳跃游戏Ⅱ - 图11出发,最远可到达下标【45】跳跃游戏Ⅱ - 图12。下标【45】跳跃游戏Ⅱ - 图13可到达的位置中,下标【45】跳跃游戏Ⅱ - 图14的值是【45】跳跃游戏Ⅱ - 图15,从下标【45】跳跃游戏Ⅱ - 图16出发可以达到更远的位置,因此第二步到达下标【45】跳跃游戏Ⅱ - 图17
image.png
在具体的实现中,我们维护当前能够到达的最大下标位置【45】跳跃游戏Ⅱ - 图19。我们从左到右遍历数组,在遍历过程中,记录在当前范围内下一次跳跃能够跳到的最远位置【45】跳跃游戏Ⅱ - 图20,当遍历到【45】跳跃游戏Ⅱ - 图21时,需要更新【45】跳跃游戏Ⅱ - 图22并将跳跃次数增加【45】跳跃游戏Ⅱ - 图23。注意,在遍历数组时,我们不访问最后一个元素,因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。但如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。

还是以上图为例,当从下标【45】跳跃游戏Ⅱ - 图24开始跳跃时,我们最远可到达下标【45】跳跃游戏Ⅱ - 图25处,此时【45】跳跃游戏Ⅱ - 图26。我们从左到右遍历数组,在下标【45】跳跃游戏Ⅱ - 图27处,我们下一次能跳的最远位置【45】跳跃游戏Ⅱ - 图28,而在下标【45】跳跃游戏Ⅱ - 图29处,我们下一次能跳的最远位置【45】跳跃游戏Ⅱ - 图30,我们「贪心」地选择可到达的最远位置,即【45】跳跃游戏Ⅱ - 图31,因此跳到下标【45】跳跃游戏Ⅱ - 图32的位置。以此类推……

代码实现

  1. public int jump(int[] nums) {
  2. if (nums == null || nums.length < 2) {
  3. return 0;
  4. }
  5. // 当前能够到达的最大下标位置
  6. int end = 0;
  7. // 下一次能够跳到的最远位置
  8. int maxPosition = 0;
  9. // 跳跃次数
  10. int steps = 0;
  11. for (int i = 0; i < nums.length - 1; i++) {
  12. // 找能跳的最远的
  13. maxPosition = Math.max(maxPosition, nums[i] + i);
  14. // 遇到边界,就更新边界为可到达的最远位置,并且步数加一
  15. if (i == end) {
  16. end = maxPosition;
  17. steps++;
  18. }
  19. }
  20. return steps;
  21. }

复杂度分析

  • 时间复杂度:【45】跳跃游戏Ⅱ - 图33,其中【45】跳跃游戏Ⅱ - 图34是数组长度。
  • 空间复杂度:【45】跳跃游戏Ⅱ - 图35