【LintCode 題解】美團面試算法題:跳躍遊戲

題目描述

給出一個非負整數數組,你最初定位在數組的第一個位置。   

數組中的每個元素代表你在那個位置可以跳躍的最大長度。    

判斷你是否能到達數組的最後一個位置。

 

樣例 1

輸入 : [2,3,1,1,4]
輸出 : true

樣例 2

輸入 : [3,2,1,0,4]
輸出 : false

題解

這個問題有兩個方法,一個是貪心和 動態規劃

貪心方法時間複雜度爲O(N)

動態規劃方法的時間複雜度爲爲O(n^2)

對於大廠面試中高頻出現的經典題,我們需要了解它的不同解法,有的大廠不喜歡你一上來就秒掉題目,而更重視你一步步根據題目優化解法,最後得到最優解的解題思路和過程。

另外即使運氣真的很不好碰到了新題,做過的高頻題也會激發你解題的思路。

1. DP1

每到一個點 i,我們掃描之前所有的點,如果之前某點j本身可達,並且與current 點可達,表示點i是可達的。

返回值:DP數組的最後一個值。

// DP1.
    public boolean canJump1(int[] A) {
        if (A == null || A.length == 0) {
            return false;
        }

        int len = A.length;
        boolean[] can = new boolean[len];
        can[0] = true;

        for (int i = 1; i < len; i++) {
            can[i] = false;
            for (int j = 0; j < i; j++) {
                // j can arrive and can jump to i.
                if (can[j] && A[j] >= i - j) {
                    can[i] = true;
                    break;
                }
            }
        }

        return can[len - 1];
    }

2. DP2

優化的點1:既然某點可達,肯定前面的點全部是可達的。這個比較好理解。因爲你到達i點肯定要經過前面的一個點,這個依次推知可知前面所有的點可達。

所以我們不需要數組來記錄結果,只要默認i點前全部可達就行了。

優化點2:如果某點不可達了,直接返回false。不需要再往後計算。

返回值:如果全部掃完仍沒有返回false,返回true。

// DP2.
    public boolean canJump2(int[] A) {
        if (A == null || A.length == 0) {
            return false;
        }

        int len = A.length;

        for (int i = 1; i < len; i++) {
            boolean can = false;
            for (int j = 0; j < i; j++) {
                // j can arrive and can jump to i.
                if (A[j] >= i - j) {
                    // 說明i是可達的,置標記位
                    can = true;
                    break;
                }
            }

            // 優化:如果某一步已經到不了了,後面的也不必再計算了.
            if (!can) {
                return false;
            }
        }

        return true;
    }

 

3. 遞歸

思想是,從前至尾掃描,至第一個距離與本點可達的點j,計算j點是否可達。使用遞歸計算j

點的可達性。

其實這裏還是用到了貪心的思維。在考慮本點是否可達的時候,我們是考慮與本點最遠的一個點是否可達。實際上這也make sense。

假設j點可以到達i點,那麼後面的點可以不管。

(1)因爲如果j點不可達,那麼j+1也不可達。如果i不可達,後面的點也可不算。

(2)如果j點可達,並且j點可到達i,那麼也沒有必要再往後計算了。因爲結論已經出來了。

  (3) 如果j點可達,但j不可達i,那麼就繼續計算。

// 3. DFS.
    public static boolean canJump3(int[] A) {
        if (A == null || A.length == 0) {
            return false;
        }

        return canJump(A, A.length - 1);
    }

    public static boolean canJump(int[] A, int index) {
        if (index == 0) {
            return true;
        }

        for (int i = 0; i <= index - 1; i++) {
            if (A[i] >= index - i) {
                return canJump(A, i);
            }
        }

        return false;
    }

4. 貪心法

我們現在來使用貪心法One pass解決此問題。

維護一個right (表示右邊能跳到的最遠的點),從左往右掃描,根據當前可跳的步驟不斷更新right ,當right到達終點,即可返回true. 若更新完right後,right未

動,並且index = right,而且這時沒到達終點,代表我們不可能到達終點了。(當前index的可跳值應該是0)。、

// solution 3: one pass.
    public boolean canJump(int[] A) {
        // 4:42
        if (A == null) {
            return false;
        }
        
        int len = A.length;

        int right = 0;        
        for (int i = 0; i < A.length; i++) {
            right = Math.max(right, i + A[i]);
            if (right == len - 1) {
                return true;
            }
            
            if (i == right) {
                return false;
            }
        }
        
        return true;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章