18. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
思路:贪心算法
问题转化为跳跃覆盖范围可不可以覆盖到终点
每次移动取最大跳跃步数,每移动一个单位,就更新最大覆盖范围。
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围)。整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
i每次移动只能在cover的范围内移动,每移动一个元素,cover得到该元素数值(新的覆盖范围)的补充,让i继续移动下去。
而cover每次只取max(该元素数值补充后范围,cover本身范围)。
如果cover大于等于终点下标,直接return true即可。
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = 0;
if (nums.size() == 1) return true; // 只有一个元素,就是能达到
for (int i = 0; i <= cover; i++) { // 注意这里是小于等于cover
cover = max(i + nums[i], cover);
if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了
}
return false;
}
};
19. 跳跃游戏II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
思路:
移动下标达到了当前覆盖的最有距离下标时,步数就要加一,来增加覆盖距离。最后的步数就是最少步数。
如果当前覆盖最远距离下标不是集合终点,步数加一,还需要继续走。
如果当前覆盖最远距离下标就是集合终点,步数不用加一,因为不能再往后走了。
// 版本一
class Solution {
public:
int jump(vector<int>& nums) {
if (nums.size() == 1) return 0;
int curDistance = 0; // 当前覆盖最远距离下标
int ans = 0; // 记录走的最大步数
int nextDistance = 0; // 下一步覆盖最远距离下标
for (int i = 0; i < nums.size(); i++) {
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标
if (i == curDistance) { // 遇到当前覆盖最远距离下标
if (curDistance != nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环
} else break; // 当前覆盖最远距离下标是集合终点,不用做ans++操作了,直接结束
}
}
return ans;
}
};
20. 跳跃游戏III
这里有一个非负整数数组arr,你最开始位于该数组的其实下标start处,你可以跳到i + arr[i]或者i - arr[i]。
判断是否能够跳到对应元素值为0的任一下标处。
注意,无论何时,都无法跳到数组之外。
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
思路:
可以使用广度优先搜索方法得到从start开始能够到达的所有位置,如果其中某个位置对应的元素为0,那么就返回true。
初始时将start加入队列。在每一次的搜索过程中,我们取出队首的节点u,它可以到达的位置为u + arr[u]和u - arr[u]。如果某个位置落在数组的下标范围[0, len(arr)]内,并且没有被搜索过,则将该位置加入队尾。在搜索结束后,如果仍然没有找到符合要求的位置,返回false。
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
if (arr[start] == 0) {
return true;
}
int n = arr.size();
vector<bool> used(n);
queue<int> q;
q.push(start);
used[start] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
if (u + arr[u] < n && !used[u + arr[u]]) {
if (arr[u + arr[u]] == 0) {
return true;
}
q.push(u + arr[u]);
used[u + arr[u]] = true;
}
if (u - arr[u] >= 0 && !used[u - arr[u]]) {
if (arr[u - arr[u]] == 0) {
return true;
}
q.push(u - arr[u]);
used[u - arr[u]] = true;
}
}
return false;
}
};
复杂度分析:
- 时间复杂度:O(N),其中 NN 是数组 arr 的长度
- 空间复杂度:O(N)。