Leetcode 45:跳躍遊戲 II(最詳細的解法!!!)
給定一個非負整數陣列,你最初位於陣列的第一個位置。
陣列中的每個元素代表你在該位置可以跳躍的最大長度。
你的目標是使用最少的跳躍次數到達陣列的最後一個位置。
示例:
輸入: [2,3,1,1,4]
輸出: 2
解釋: 跳到最後一個位置的最小跳躍數是 2。
從下標為 0 跳到下標為 1 的位置,跳 1 步,然後跳 3 步到達陣列的最後一個位置。
說明:
假設你總是可以到達陣列的最後一個位置。
解題思路
這個問題是之前Leetcode 55:跳躍遊戲(最詳細的解法!!!)的提升。我們先看看這個問題能不能通過動態規劃解決,很簡單,只是對之前的問題稍加修改,我們這裡要考慮的問題是到index
[0:index-1]
中向前一步能到達index
的最小值。
我們在初始化的時候將mem
全部初始化為inf
,而mem[0]=0
即可。
class Solution:
def jump(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums_len = len(nums)
mem = [float('inf')]*nums_len
mem[0] = 0
for i in range (1,nums_len):
for j in range(i):
if nums[j] + j >= i:
mem[i] = min(mem[j]+1, mem[i])
break
return mem[-1]
但是這樣寫法超時了,原因和之前問題一樣,一定是有些可以剪枝的思路沒有考慮到。我們如果按照之前思路,將這個問題反過來考慮的話,也就是
class Solution:
def jump(self, nums) :
"""
:type nums: List[int]
:rtype: int
"""
nums_len = len(nums)
mem = [float('inf')]*nums_len
mem[0] = 0
for i in range(1,nums_len):
for j in range(i, -1, -1):
if mem[j] != float('inf') and nums[j] + j >= i:
mem[i] = min(mem[j]+1, mem[i])
break
return mem[-1]
但是這樣思考對嗎?對於上面的例子
2 3 1 1 4
0 1
我們考慮1
,如果用我們上面寫的程式碼的話,這裡我們就需要2
步到1
,而實際上只要1
步,原因在於我們break
了,我們沒有繼續向前查詢更小的了。break
去了就可以了嗎?實際上這又回到了最原始的程式碼,沒有任何剪枝的考量,顯然是不合理的。
這個問題通過貪心演算法能否解決呢?我們前面步子儘量邁大一點,後面就有更多的空間了啊?顯然這種做法是不可行的,因為我們每次求解的是區域性最優解,而對於全域性來說就不一定是最優解了。那是不是不能用貪心了?我們可以換一個思路,我們先思考跳一步的話最遠可以跳多遠,接著思考跳兩步的話最遠可以跳多遠,以此類推直到最遠的距離大於等於nums.size()-1
,那麼此時的步數自然就是最少的步數。
所以我們先遍歷一遍nums
,對於每個index
,我們要判斷當前steps[step]
是不是能大於等於nums_len-1
,如果成立,那麼我們只要step+1
步即可。如果steps[step]<index
,也就是說當前step
步無法到達index
這個位置,我們就要多跳一步step+1
。對於我們下一步可以跳多遠steps[step+1]
這個問題,自然是i in range(index)
這個區間內max(nums[i]+i)
所決定的,這也是之前問題Leetcode 55:跳躍遊戲(最詳細的解法!!!)中的思想。
class Solution:
def jump(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums_len = len(nums)
steps, step = [nums[0]]*nums_len, 0
for i in range(1, nums_len):
if steps[step] >= nums_len - 1:
return step + 1
if steps[step + 1] < i + nums[i]:
steps[step + 1] = i + nums[i]
if steps[step] <= i:
step += 1
return step
非常簡潔,非常酷!!!
我將該問題的其他語言版本新增到了我的GitHub Leetcode
如有問題,希望大家指出!!!