LeetCode 45. Jump Game II

139 阅读1分钟

LeetCode 45. Jump Game II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i]  i + j < n 返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

 

示例 1:

输入: nums = [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。   从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 示例 2:

输入: nums = [2,3,0,1,4] 输出: 2  

提示:

1 <= nums.length <= 104 0 <= nums[i] <= 1000 题目保证可以到达 nums[n-1]

算法1

(动态规划,贪心) O(n)
设状态 f(i) 表示到达 i 所需要的最少步数。
f(0)=0,其余待定。定义辅助指针 last 为第一次到达 i 时上一步的位置,last 从 0 开始。
通过归纳可以得知,从小到大遍历过程中,第一次通过 x 可以到达 y 时,y 的最少步数就是 x 的最少步数加 1。因为一次移动,可以从 x 到 [x+1,x+nums(x)] 的任意位置,所以根据归纳(即 x 也是这样到达的),这样一定是最优的。
根据以上得知,令 f(i)=f(last)+1 后,f(i) 就会是最优值。
故可以根据 i 来让 last 向后移动,找到最早的可以一步到达 i 的位置,然后根据 f(last) 更新 f(i)。

时间复杂度

数组的每个元素最多被遍历两次,故时间复杂度为 O(n)。

空间复杂度

需要 O(n) 的额外空间存储状态。

C++ 代码

class Solution {
public:
    int jump(vector<int>& nums) {
        const int n = nums.size();
        vector<int> f(n);
        f[0] = 0;
        int last = 0;

        for (int i = 1; i < n; i++) {
            // 依次求 f[i] 的值。
            while (i > last + nums[last]) // 根据 i 来更新 last。
                last++;

            f[i] = f[last] + 1; // 根据 f[last] 更新 f[i]。
        }

        return f[n - 1];
    }
};

算法2

(贪心,双指针) O(n)

定义指针 cur 和 dis,分别表示当前位置和可达到的最远位置。定义一轮遍历过程如下:
移动 cur 到 dis,途中记录可以达到的最远位置 next。
答案加 1。
更新 dis 为 next。
当 dis 大于等于 n−1 时视为到达,返回答案。

时间复杂度

数组的每个元素最多被遍历两次,故时间复杂度为 O(n)。

空间复杂度

仅需要常数的额外空间。

C++ 代码

class Solution {
public:
    int jump(vector<int>& nums) {
        const int n = nums.size();
        int ans = 0, cur = 0, dis = 0;

        while (dis < n - 1) {
            int next = 0;
            while (cur <= dis) {
                next = max(next, cur + nums[cur]);
                cur++;
            }

            ans++;
            dis = next;
        }

        return ans;
    }
};