題目描述
給出一個非負整數數組,你最初定位在數組的第一個位置。
數組中的每個元素代表你在那個位置可以跳躍的最大長度。
判斷你是否能到達數組的最後一個位置。
樣例 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;
}