【leetcode】45. 跳跃游戏 II

题目内容

45. 跳跃游戏 II

思路分析

本题难度为困难,是55. 跳跃游戏的plus版。55题是让判断是否能到达,使用了贪心算法(尽量跳的远)。

第一眼看到这个题时,算法课学艺不精的我,打算用动态规划来做,感觉应该有可以重复利用的子问题,结果栈溢出,不断地迭代而不退出,翻车了....

本题的正确解法应该为贪心算法,有两种贪心方式,对应着两个方法。

方法一

从终点倒着往回推,每次选择离终点最远的点,也就是下标最小的点(贪心)。因此每次需要从小到大遍历数组,找到第一个可以到达当前点的点。因此算法时间复杂度为O(N^2),空间复杂度为O(1)。

(而从前往后贪就不行?这可能就是算法课上说的贪心算法不直观吧,比如下图中的工作安排问题的不同贪心方式只有一个可用)

由于官方题解中说C++实现这种方法会超时,故没有去实现。

方法二

从前往后遍历,每次从当前可以到达的点中,选出下一跳可以到达的最远点,如下图所示(图来自官方题解)。

代码实现时,只需要维护当前可以到达的最远距离reach和下一跳可以到达的next_reach最远距离,并从前往后遍历数组。每次到达当前最远距离时,将reach更新为next_reach,并将跳数+1。由于是起跳时加跳数,因此不遍历终点(到达终点后不再起跳,因此不加条数)。使用这样的方法,将算法的时间复杂度降为了O(N)。

代码

#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <algorithm>

using namespace std;

class Solution {
public:
    // 官方解法 贪心算法
    // 时间复杂度O(N),空间复杂度O(1)
    int jump(vector<int>& nums) {
        int dst = nums.size() - 1;
        int reach{ 0 };  // 能到达的最大边界
        int next_reach{ 0 };  // 下一次能到达的最大边界
        int jump_num{ 0 };  // 跳跃次数

        for (int i = 0; i < dst; i++) {  // 起跳前加步数,因此不遍历最后一个点,避免多加步数
            next_reach = max(i + nums[i], next_reach);
            if (i == reach) {
                jump_num++;
                reach = next_reach;
            }
        }

        return jump_num;

    }
    
    



    // !!! 以下两个函数是自己的错误解法 想用动态规划 翻车 !!!
    // 从src至dst所需的最少跳跃次数
    int min_jump_num(vector<int>& nums, int src, int dst) {
        // 边界条件
        if (src == dst) {
            return 0;
        }
        
        if (src == 0 && dst != nums.size() - 1) {
            if (src + nums[src] >= dst) {
                return 1;
            }
            else {
                return INT_MAX;  // 一跳不可达 
            }
        }

        int local_min_jump_num{ INT_MAX };
        for (int i = src; i <= dst; ++i) {
            // 可以一步到达dst
            if (i + nums[i] >= dst) {
                int temp = min_jump_num(nums, i, dst);
                if (temp < local_min_jump_num) {
                    local_min_jump_num = temp + 1;
                }
            }
            // 不可以一步到达dst则不计算(会在递归的过程中计算)
        }
        return local_min_jump_num;
    }
    int false_jump(vector<int>& nums) {
        return min_jump_num(nums, 0, nums.size() - 1);
    }
};


int main() {
    Solution s;
    vector<int> input{ 2,3,1,1,4 };

    int output;
    output = s.jump(input);

    return output;
}

收获

学到了一种贪心算法