跳跃游戏系列

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)。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值