1. 程式人生 > >[Week 7] LeetCode 45 & 55. Jump Game I & II

[Week 7] LeetCode 45 & 55. Jump Game I & II

LeetCode 45 & 55. Jump Game I & II

Jump Game I

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1

Input:
[2,3,1,1,4] Output: true Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

題解

這個問題我們可以理解成這樣:我們從index=0開始,掃描整個陣列,每次更新當前所能覆蓋的最遠距離,最後判斷是否覆蓋了終點即可。

例1:(我們用O表示當前陣列中能跳躍到的位置)

  1. index=0: [O,O,O,X,X]
  2. index=1: [O,O,O,O,O]

例2:(我們最多隻能覆蓋到[0,3]

  1. index=0: [O,O,O,O,X]
  2. index=1: [O,O,O,O,X]
  3. index=2: [O,O,O,O,X]
  4. index=3: [O,O,O,O,X]

看起來很簡單對吧,我們可以把這樣的演算法叫貪心演算法(採取步步逼近的方式構造問題的解,其下一步的選擇總是在當前看來收效最快和效果最明顯的哪一個)

因為這個題相對比較簡單就直接上程式碼了。

Code

class Solution {
public:
    bool canJump(vector<int>& nums) {
        // rightMost is the farthest index we can reach
        int rightMost = 0;
        for (int i = 0; i < nums.size() - 1 && rightMost < nums.size(); ++i) {
            if (rightMost < i) break;
            rightMost = max(rightMost, i + nums[i]);
        }
        return rightMost >= nums.size() - 1;
    }
};

複雜度分析

程式碼和想法都是如此簡單,只掃描一次陣列,即時間複雜度為O(n)

Jump Game II

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

Example

Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
    Jump 1 step from index 0 to 1, then 3 steps to the last index.

Note:

You can assume that you can always reach the last index.

題解

相比Jump Game I,這個題假設了能夠到達,但要我們計算最少幾次跳能到達終點。

又是這種類似最短路徑的問題,我們可以用BFS來暴力搜解,但對於這種只求步數問題,用BFS是需要消耗大量空間的,事實證明這題是卡了空間的,暴力只會送你Memory Limit Exceeded

那我們來想想能不能用上一題的思路來求解,想象一下自己站在一個區間內:[begin, reached],在這個區間內你能到達的最遠距離為nextReached,意思就是你下一條的區間為:[reached + 1, nextReached],然後再執行上述操作,直到你的所在區間包含終點為止。我們的演算法可以概括為一下步驟:

  1. 掃描當前所在區間:[begin, reached],找到在這個區間內能跳到的最遠距離nextReached
  2. 掃描完後,將區間替換為[reached + 1, nextReached],再執行步驟1直到區間包含終點為止

為什麼正確?

每個區間代表著你上一跳能達到的所有位置,拿上面的示例來說:

  1. 第一個區間:[0,0],下一跳區間:[1,2]
  2. 第二個區間:[1,2],下一跳區間:[3,4](達到終點)

這其實也是一種貪心的策略,每次都將陣列劃分成更小的陣列,且每次決策都是對當前來說最好的,例如上面的示例中,如果你跳的路徑為:0->2->...而不是0->1->4,那麼跳的次數一定會更多。

Code

class Solution {
public:
    int jump(vector<int> &nums)
    {
      int times = 0, reached = 0, nextReached = 0;
      for (int i = 0; i < nums.size() - 1; ++i)
      {
        if (i > reached)
          return -1;
        if (reached >= nums.size() - 1)
          return times;
        nextReached = max(nextReached, i + nums[i]);
        if (i == reached)
        {
          times++;
          reached = nextReached;
        }
      }
      return times;
    }
};

複雜度分析

同上題一樣,我們做的工作只有掃描整個陣列,只不過多維護了兩個變數罷了,所以時間複雜度依然為O(n)。而且比起BFS,其空間複雜度也是要好得多,只是維護了兩個變數而已。