Hard!html
題目描述:算法
給定一個非負整數數組,你最初位於數組的第一個位置。數組
數組中的每一個元素表明你在該位置能夠跳躍的最大長度。優化
你的目標是使用最少的跳躍次數到達數組的最後一個位置。this
示例:spa
輸入: [2,3,1,1,4] 輸出: 2 解釋: 跳到最後一個位置的最小跳躍數是 。 從下標爲 0 跳到下標爲 1 的位置,跳 步,而後跳 步到達數組的最後一個位置。 213
說明:3d
假設你老是能夠到達數組的最後一個位置。code
解題思路:htm
這題是以前那道Jump Game 跳躍遊戲 的延伸,那題是問能不能到達最後一個數字,而此題只讓咱們求到達最後一個位置的最少跳躍數,貌似是默認必定能到達最後位置的? 此題的核心方法是利用貪婪算法Greedy的思想來解,想一想爲何呢? 爲了較快的跳到末尾,咱們想知道每一步能跳的範圍,這裏貪婪並非要在能跳的範圍中選跳力最遠的那個位置,由於這樣選下來不必定是最優解,這麼一說感受又有點不像貪婪算法了。咱們這裏貪的是一個能到達的最遠範圍,咱們遍歷當前跳躍能到的全部位置,而後根據該位置上的跳力來預測下一步能跳到的最遠距離,貪出一個最遠的範圍,一旦當這個範圍到達末尾時,當前所用的步數必定是最小步數。blog
咱們須要兩個變量cur和pre分別來保存當前的能到達的最遠位置和以前能到達的最遠位置,只要cur未達到最後一個位置則循環繼續,pre先賦值爲cur的值,表示上一次循環後能到達的最遠位置,若是當前位置i小於等於pre,說明仍是在上一跳能到達的範圍內,咱們根據當前位置加跳力來更新cur,更新cur的方法是比較當前的cur和i + A[i]之中的較大值,若是題目中未說明是否能到達末尾,咱們還能夠判斷此時pre和cur是否相等,若是相等說明cur沒有更新,即沒法到達末尾位置,返回-1。
C++解法一:
1 class Solution { 2 public: 3 int jump(vector<int>& nums) { 4 int res = 0, n = nums.size(), i = 0, cur = 0; 5 while (cur < n - 1) { 6 ++res; 7 int pre = cur; 8 for (; i <= pre; ++i) { 9 cur = max(cur, i + nums[i]); 10 } 11 if (pre == cur) return -1; // May not need this 12 } 13 return res; 14 } 15 };
還有一種寫法,跟上面那解法略有不一樣,可是本質的思想仍是同樣的,關於此解法的詳細分析可參見http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html。這裏cur是當前能到達的最遠位置,last是上一步能到達的最遠位置,咱們遍歷數組,首先用i + nums[i]更新cur,這個在上面解法中講過了,而後判斷若是當前位置到達了last,即上一步能到達的最遠位置,說明須要再跳一次了,咱們將last賦值爲cur,而且步數res自增1,這裏咱們小優化一下,判斷若是cur到達末尾了,直接break掉便可。
C++解法二:
1 class Solution { 2 public: 3 int jump(vector<int>& nums) { 4 int res = 0, n = nums.size(), last = 0, cur = 0; 5 for (int i = 0; i < n - 1; ++i) { 6 cur = max(cur, i + nums[i]); 7 if (i == last) { 8 last = cur; 9 ++res; 10 if (cur >= n - 1) break; 11 } 12 } 13 return res; 14 } 15 };
要理解這個算法,首先明白,這個題只要咱們求跳數,怎麼跳,最後距離是多少,都沒讓求的。
大牛這個算法的思想主要是,掃描數組(廢話。。。),以肯定當前最遠能覆蓋的節點,放入curr。而後繼續掃描,直到當前的路程超過了上一次算出的覆蓋範圍,那麼更新覆蓋範圍,同時更新條數,由於咱們是通過了多一跳才能繼續前進的。
形象地說,這個是在爭取每跳最遠的greedy。舉個栗子。
好比就是咱們題目中的[2,3,1,1,4]。初始狀態是這樣的:cur表示最遠能覆蓋到的地方,用紅色表示。last表示已經覆蓋的地方,用箭頭表示。它們都指在第一個元素上。
接下來,第一元素告訴cur,最遠咱能夠走2步。因而:
下一循環中,i指向1(圖中的元素3),發現,哦,i小於last能到的範圍,因而更新last(至關於說,進入了新的勢力範圍),步數ret加1.同時要更新cur。由於最遠距離發現了。
接下來,i繼續前進,發現i在當前的勢力範圍內,無需更新last和步數ret。更新cur。
i繼續前進,接下來發現超過當前勢力範圍,更新last和步數。cur已然最大了。
最後,i到最後一個元素。依然在勢力範圍內,遍歷完成,返回ret。
這道題讓咱們明白一個道理:
不要作無必要的計算。
對了,有同窗會問,那爲啥要用last,直接用curr跳不就好了。直接用curr跳那每次都是跳最遠的,可是最優路徑不不必定是這樣。該算法時間複雜度爲O(n)。
C++解法三:
1 /* 2 * We use "last" to keep track of the maximum distance that has been reached 3 * by using the minimum steps "ret", whereas "curr" is the maximum distance 4 * that can be reached by using "ret+1" steps. Thus, 5 * curr = max(i+A[i]) where 0 <= i <= last. 6 */ 7 class Solution { 8 public: 9 int jump(int A[], int n) { 10 int ret = 0; 11 int last = 0; 12 int curr = 0; 13 for (int i = 0; i < n; ++i) { 14 if (i > last) { 15 last = curr; 16 ++ret; 17 } 18 curr = max(curr, i+A[i]); 19 } 20 21 return ret; 22 } 23 };