55. Jump Game(跳跃游戏)三种解法(C++ & 注释)

55. Jump Game(跳跃游戏)

  • 1. 题目描述
  • 2. 回溯法(Backtracking, Time Limit Exceeded)
    • 2.1 解题思路
    • 2.2 实例代码
  • 3. 动态规划(Dynamic Programming)
    • 3.1 解题思路
    • 3.2 实例代码
  • 4. 贪心算法(Greedy)
    • 4.1 解题思路
    • 4.2 实例代码
  • 5. 参考资料

1. 题目描述

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 ,所以你永远不可能到达最后一个位置。

题目链接:中文题目;英文题目

2. 回溯法(Backtracking, Time Limit Exceeded)

2.1 解题思路

对于这道题,回朔法是非常直接的思路:

  1. 从序号(index) = 0开始,取nums[index]的最大距离,跳到下一个index + nums[index]序号位置;
  2. 如果下一个nums[index]大于0,则继续1. 的步骤,直到抵到右边界的序号,返回true;
  3. 反之,如果下一个nums[index]为0,则返回上一个序号位置,取第二大的距离,跳到下一个序号,继续1. 的步骤,直到回到初始序号,此时表示不可能抵达右边界的序号,返回false;

下面的流程图直观地表示了上面的步骤:
55. Jump Game(跳跃游戏)三种解法(C++ & 注释)_第1张图片

当然1. 可以先取最小值,再取最大值,但是这样会稍微慢一些,我们可以优先取最大值,保证每次的移动的距离最大。

可惜的是,这个方法对于某些特殊的test case是会超时的,一般都是右边有0,比如下面两:1) test case1;2)test case2

2.2 实例代码

class Solution {
    bool backTracking(vector<int>& nums, int i) {
        if (i + nums[i] >= static_cast<int>(nums.size()) - 1) return true; // static_cast - 因为需要用unsigned - 1,转换成int比较保险,否则0 - 1会溢出
        // if (!nums[i]) return false; // 添加这句可以通过test case1,但是另一个不行
        for (int j = nums[i]; j > 0; j--) if (backTracking(nums, j + i)) return true;
        return false;
    }

public:
    bool canJump(vector<int>& nums) { return backTracking(nums, 0); }
};

3. 动态规划(Dynamic Programming)

3.1 解题思路

动态规划有两种思路:1)使用动态规划的回朔法;2)动态规划;

首先,我们先来看看2. 回溯法里面的第二个例子,我们发现1(序号2)无论如何都会走到0(序号3),2(序号1)也是最终都会走到0,无论是直接走两步来到0,还是先走到1,再走到0。所以我们知道这两个序号是不可能抵到终点,所以3(序号1)取到这两个序号的值一定是无效的,所以如果在之前回溯的过程中,我们记录下那些不可能抵达终点的序号和可以抵达终点的序号,分别标记为:GOON,NOWAY,如果来到这些有标记的点,GOON直接返回true,NOWAY直接返回false,这样就省去了一些递归过程。

对于没有标记的点,我们可以统一标记为UNKNOWN,正常进行2. 的回溯即可。具体的流程图如下:
55. Jump Game(跳跃游戏)三种解法(C++ & 注释)_第2张图片
可惜C++版本的这个思路还是超时~

但是走到这里,我们观察到一个重要的线索:如果我们能从后面开始动态规划,从后面记录下每个点是否能抵达终点。所以根据这个思路,我们可以写出动态规划的方程:

dp[i] = dp[i + k], 1 < k <= nums[i]

我们以下面这个例子来进行说明:

[0, 1, 2, 3, 4, 5, 6]
[2, 4, 2, 1, 0, 2, 0]

dp[5]能否达到6,取决于dp[5 + 1], dp[5 + 2](越界忽略),dp[6]默认能达到终点,所以dp[5] = GOON;以此类推,最后我们只需检查dp[0]是否等于GOON即可。具体的流程见下图:
55. Jump Game(跳跃游戏)三种解法(C++ & 注释)_第3张图片

3.2 实例代码

typedef enum explores { GOON, NOWAY, UNKNOWN } explores;

class Solution {
    // 下面两个方法二选一即可
    // 1.使用动态规划的回朔法, Time Limit Exceeded
    bool backTrackingUsingDPTopDown(vector<int>& nums, int i, vector<explores>& indices) {
        if (indices[i] != UNKNOWN) return indices[i] == GOON ? true : false;
        if (i + nums[i] >= static_cast<int>(nums.size()) - 1) { indices[i] = GOON; return true; }
        for (int j = nums[i]; j > 0; j--) if (backTrackingUsingDPTopDown(nums, j + i, indices)) { indices[i] = GOON; return true; }
        indices[i] = NOWAY; return false;
    }

    // 2. 动态规划
    bool UsingDPBottomUp(vector<int>& nums, vector<explores>& indices) {
        indices[nums.size() - 1] = GOON;
        for (int i = nums.size() - 2; i >= 0; i--) {
            int furestJump = min(nums[i] + i, static_cast<int>(nums.size()) - 1);
            for (int j = i + 1; j <= furestJump; j++) if (indices[j] == GOON) { indices[i] = GOON; break; }
        }
        return indices[0] == GOON;
    }

public:
    bool canJump(vector<int>& nums) {
        vector<explores> indices(nums.size(), UNKNOWN);
        //return UsingDPBottomUp(nums, indices);
        return UsingDPBottomUp(nums, indices);
    }
};

4. 贪心算法(Greedy)

4.1 解题思路

最后,我们来看看第二个动态规划的方法。我们发现,序号5可以抵到终点,所以我们可以往前找,只要有序号能抵达序号5,就能顺藤摸瓜达到终点。所以序号1可以达到5,之后序号0可以达到序号1,因而必有一条路径直通终点!根据这个思路,我们总结出下面的步骤:

  1. 初始化TargetIdx = 终点序号,从后往前遍历,如果有nums[i] >= targetIdx - i,则有点必然达到TargetIdx;
  2. 更新TargetIdx = i,继续向前寻找;
  3. 结束时如果TargetIdx == 0,说明能从起点序号到终点序号,返回TRUE;反之,返回False;

4.2 实例代码

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int targetIdx = nums.size() - 1;
        for (int i = targetIdx - 1; i >= 0; i--) if (nums[i] >= targetIdx - i) targetIdx = i;
        return targetIdx == 0;
    }
};

5. 参考资料

  1. 55. Jump Game

你可能感兴趣的:(LeetCode-Medium)