|
1 |
| -## 题目链接 |
2 |
| -https://leetcode-cn.com/problems/jump-game-ii/ |
| 1 | +> 相对于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不少,做好心里准备! |
3 | 2 |
|
4 |
| -## 思路 |
| 3 | +# 45.跳跃游戏II |
5 | 4 |
|
6 |
| -本题相对于[0055.跳跃游戏](https://github.com/youngyangyang04/leetcode/blob/master/problems/0053.最大子序和.md)还是难了不少。 |
| 5 | +题目地址:https://leetcode-cn.com/problems/jump-game-ii/ |
7 | 6 |
|
8 |
| -本题要计算最大步数,那么就要想清楚什么时候步数加一? |
| 7 | +给定一个非负整数数组,你最初位于数组的第一个位置。 |
9 | 8 |
|
10 |
| -**这里需要统计两个距离,当前可移动距离和下一步最远距离**。 |
| 9 | +数组中的每个元素代表你在该位置可以跳跃的最大长度。 |
| 10 | + |
| 11 | +你的目标是使用最少的跳跃次数到达数组的最后一个位置。 |
11 | 12 |
|
12 |
| -如果移动范围超过当前可移动距离,那么就必须再走一步来达到增加可移动距离的目的。 |
| 13 | +示例: |
| 14 | +输入: [2,3,1,1,4] |
| 15 | +输出: 2 |
| 16 | +解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 |
| 17 | + |
| 18 | +说明: |
| 19 | +假设你总是可以到达数组的最后一个位置。 |
| 20 | + |
| 21 | + |
| 22 | +# 思路 |
| 23 | + |
| 24 | +本题相对于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)还是难了不少。 |
| 25 | + |
| 26 | +但思路是相似的,还是要看最大覆盖范围。 |
| 27 | + |
| 28 | +本题要计算最小步数,那么就要想清楚什么时候步数才一定要加一呢? |
| 29 | + |
| 30 | +贪心的思路,局部最优:当前可移动距离尽可能多走,如果还没到终点,步数再加一。整体最优:一步尽可能多走,从而达到最小步数。 |
| 31 | + |
| 32 | +思路虽然是这样,但在写代码的时候还不能真的就能跳多远跳远,那样就不知道下一步最远能跳到哪里了。 |
| 33 | + |
| 34 | +**所以真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最小步数!** |
| 35 | + |
| 36 | +**这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖**。 |
| 37 | + |
| 38 | +如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。 |
13 | 39 |
|
14 | 40 | 如图:
|
15 | 41 |
|
16 |
| -<img src='../pics/45.跳跃游戏II.png' width=600> </img></div> |
| 42 | + |
17 | 43 |
|
18 |
| -### 方法一 |
| 44 | +**图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)** |
19 | 45 |
|
20 |
| -这里还是有个特殊情况需要考虑,如果当前可移动距离的终点就是是集合终点,那么就不用增加步数了,因为不能再往后走了。 |
| 46 | +## 方法一 |
21 | 47 |
|
22 |
| -详情可看代码(详细注释) |
| 48 | +从图中可以看出来,就是移动下标达到了当前覆盖的最远距离下标时,步数就要加一,来增加覆盖距离。最后的步数就是最少步数。 |
23 | 49 |
|
24 |
| -``` |
| 50 | +这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时 |
| 51 | + |
| 52 | +* 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。 |
| 53 | +* 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。 |
| 54 | + |
| 55 | +C++代码如下:(详细注释) |
| 56 | + |
| 57 | +```C++ |
25 | 58 | // 版本一
|
26 | 59 | class Solution {
|
27 | 60 | public:
|
28 | 61 | int jump(vector<int>& nums) {
|
29 | 62 | if (nums.size() == 1) return 0;
|
30 |
| - int curDistance = 0; // 当前可移动距离 |
| 63 | + int curDistance = 0; // 当前覆盖最远距离下标 |
31 | 64 | int ans = 0; // 记录走的最大步数
|
32 |
| - int nextDistance = 0; // 下一步最远距离 |
| 65 | + int nextDistance = 0; // 下一步覆盖最远距离下标 |
33 | 66 | for (int i = 0; i < nums.size(); i++) {
|
34 |
| - nextDistance = max(nums[i] + i, nextDistance); |
35 |
| - if (i == curDistance) { // 遇到当前可移动距离的终点 |
36 |
| - if (curDistance != nums.size() - 1) { // 如果当前可移动距离的终点不是集合终点 |
37 |
| - ans++; // 需要走下一步 |
38 |
| - curDistance = nextDistance; // 更新下一步最远距离的范围 |
39 |
| - if (nextDistance >= nums.size() - 1) break; // 下一步最远距离已经可以达到终点,结束循环 |
40 |
| - } else break; // 当前可移动距离的终点是集合终点 |
| 67 | + nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标 |
| 68 | + if (i == curDistance) { // 遇到当前覆盖最远距离下标 |
| 69 | + if (curDistance != nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点 |
| 70 | + ans++; // 需要走下一步 |
| 71 | + curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了) |
| 72 | + if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环 |
| 73 | + } else break; // 当前覆盖最远距离下标是集合终点,不用做ans++操作了,直接结束 |
41 | 74 | }
|
42 | 75 | }
|
43 | 76 | return ans;
|
44 | 77 | }
|
45 | 78 | };
|
46 |
| -``` |
| 79 | +``` |
47 | 80 |
|
48 |
| -### 方法二 |
| 81 | +## 方法二 |
49 | 82 |
|
50 | 83 | 依然是贪心,思路和方法一差不多,代码可以简洁一些。
|
51 | 84 |
|
52 |
| -在方法一种,处理 当前可移动距离的终点 是不是集合终点 来判断ans是否要做相应的加一操作。 |
| 85 | +**针对于方法一的特殊情况,可以统一处理**,即:移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不考虑是不是终点的情况。 |
53 | 86 |
|
54 |
| -其实可以用 for循环遍历的时候i < nums.size() - 1,这样就是默认最后一步,一定是可以到终点的。 |
| 87 | +想要达到这样的效果,只要让移动下标,最大只能移动到nums.size - 2的地方就可以了。 |
55 | 88 |
|
56 |
| -代码如下: |
| 89 | +因为当移动下标指向nums.size - 2时: |
57 | 90 |
|
58 |
| -``` |
| 91 | +* 如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图: |
| 92 | + |
| 93 | +
|
| 94 | +* 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图: |
| 95 | +
|
| 96 | + |
| 97 | +
|
| 98 | +代码如下: |
59 | 99 |
|
| 100 | +```C++ |
| 101 | +// 版本二 |
60 | 102 | class Solution {
|
61 | 103 | public:
|
62 | 104 | int jump(vector<int>& nums) {
|
63 |
| - int curDistance = 0; |
64 |
| - int ans = 0; // 记录走的最大步数,初始为0 |
65 |
| - int nextDistance = 0; // 每走一步获得的跳跃范围 |
66 |
| - for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1 |
67 |
| - nextDistance = max(nums[i] + i, nextDistance); |
68 |
| - if (i == curDistance) { // 遇到本次跳跃范围的终点 |
69 |
| - curDistance = nextDistance; |
| 105 | + int curDistance = 0; // 当前覆盖的最远距离下标 |
| 106 | + int ans = 0; // 记录走的最大步数 |
| 107 | + int nextDistance = 0; // 下一步覆盖的最远距离下标 |
| 108 | + for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在 |
| 109 | + nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标 |
| 110 | + if (i == curDistance) { // 遇到当前覆盖的最远距离下标 |
| 111 | + curDistance = nextDistance; // 更新当前覆盖的最远距离下标 |
70 | 112 | ans++;
|
71 | 113 | }
|
72 | 114 | }
|
73 | 115 | return ans;
|
74 | 116 | }
|
75 | 117 | };
|
76 | 118 | ```
|
| 119 | + |
| 120 | +可以看出版本二的代码相对于版本一简化了不少! |
| 121 | + |
| 122 | +其精髓在于控制移动下标i只移动到nums.size() - 2的位置,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。 |
| 123 | + |
| 124 | +# 总结 |
| 125 | + |
| 126 | +相信大家可以发现,这道题目相当于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不止一点。 |
| 127 | + |
| 128 | +但代码又十分简单,贪心就是这么巧妙。 |
| 129 | + |
| 130 | +理解本题的关键在于:**以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点**,这个范围内最小步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。 |
| 131 | + |
| 132 | +就酱,如果感觉「代码随想录」很不错,就分享给身边的朋友同学吧! |
| 133 | + |
| 134 | + |
| 135 | +> **我是[程序员Carl](https://github.com/youngyangyang04),可以找我[组队刷题](https://img-blog.csdnimg.cn/20201115103410182.png),也可以在[B站上找到我](https://space.bilibili.com/525438321),本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已收录,更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在公众号:[代码随想录](https://img-blog.csdnimg.cn/20201124161234338.png),关注后就会发现和「代码随想录」相见恨晚!** |
| 136 | + |
| 137 | +**如果感觉题解对你有帮助,不要吝啬给一个👍吧!** |
| 138 | + |
0 commit comments