1. 程式人生 > >Leetcode 55:跳躍遊戲(最詳細的解法!!!)

Leetcode 55:跳躍遊戲(最詳細的解法!!!)

給定一個非負整數陣列,你最初位於陣列的第一個位置。

陣列中的每個元素代表你在該位置可以跳躍的最大長度。

判斷你是否能夠到達最後一個位置。

示例 1:

輸入: [2,3,1,1,4]
輸出: true
解釋: 從位置 0 到 1 跳 1 步, 然後跳 3 步到達最後一個位置。

示例 2:

輸入: [3,2,1,0,4]
輸出: false
解釋: 無論怎樣,你總會到達索引為 3 的位置。但該位置的最大跳躍長度是 0 , 所以你永遠不可能到達最後一個位置。

解題思路

這個問題非常簡單,我們通過遞迴很容易解決。我們想要知道能否到達index,那麼只需要知道index

之前的元素是不是有nums[i]+i >= index for i in range(index),也就是index之前是不是有元素可以到達index,如果存在,那麼我們只需要判斷是否有元素能到i,如果也成立,那麼我們返回True,否則我們返回False。例如

3    2    1    0    4
|<-  i       ->|   index
class Solution:
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
nums_len = len(nums) return self._canJump(nums, nums_len - 1) def _canJump(self, nums, index): if index == 0: return True for i in range(index): if nums[i] + i >= index and self._canJump(nums, i): return True
return False

我們也很容易通過記憶化搜尋的方法對這個問題優化

class Solution:
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums_len = len(nums)
        mem = [None]*nums_len
        mem[0] = True
        return self._canJump(nums, nums_len - 1, mem)
            
    def _canJump(self, nums, index, mem):
        if mem[index] != None:
            return mem[index]

        for i in range(index):
            if nums[i] + i >= index and self._canJump(nums, i, mem):
                mem[i] = True
                return True
        mem[i] = False
        return False

但是這種做法在提交的時候超時了,我們只能想其他的辦法。有沒有更好的呢?我們想想通過動態規劃能否解決這個問題,其實思路和記憶化搜尋是一樣的。

class Solution:
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums_len = len(nums)
        mem = [False]*nums_len
        mem[0] = True
        for i in range(1,nums_len):
            for j in range(i):
                if mem[j] and nums[j] + j >= i:
                    mem[i] = True
                    break
        
        return mem[-1]

所以可想而知這樣做也就超時了,顯然我們應該是忽略了什麼可以剪枝的條件。實際上,我們只要反過來思考就可以了。對於能否到達index的這個問題,我們需要知道對於0<=i<index是否可以到達index,並且同時我們能否到達i。這個時候,我們i不是從頭開始考慮,而是從後往前考慮的話,會節省大量的時間。例如

1   1   1   1   1   1
                   index
               true ?

我們這是時候知道可以到達index-1了,我們只需要知道index-1能否到達index就可以啦。而不是又要從頭開始遍歷一次。所以只要將程式碼稍微修改一下即可。

class Solution:
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums_len = len(nums)
        mem = [False]*nums_len
        mem[0] = True
        for i in range(1,nums_len):
            for j in range(i,-1,-1):
                if mem[j] and nums[j] + j >= i:
                    mem[i] = True
                    break
        
        return mem[-1]

同樣的修改對於記憶化搜尋也是有效的。

對於這個問題最好的解法是貪心演算法。對於index的位置,我們將index之前的元素值和其索引值相加

3   2   1   0   4
3   3   3   3 index

然後我們計算之前和的最大值m,如果index>m,那麼自然不能到達index了。對於所有[0:len(nums))都滿足上述條件的話,那麼自然就成立了。

總體的思路就是,我們希望前面的步子邁大一點,這樣可以給後面留有更多的餘地。

class Solution:
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        m = 0
        for i, num in enumerate(nums):
            if i > m:
                return False
            m = max(m, i + num)
        return True

我將該問題的其他語言版本新增到了我的GitHub Leetcode

如有問題,希望大家指出!!!