給定乙個非負整數陣列,你最初位於陣列的第乙個位置。陣列中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最後乙個位置。
示例 1:
輸入: [2,3,1,1,4]
輸出: true
解釋: 我們可以先跳 1 步,從位置 0 到達 位置 1, 然後再從位置 1 跳 3 步到達最後乙個位置。
示例 2:
輸入: [3,2,1,0,4]
輸出: false
解釋: 無論怎樣,你總會到達索引為 3 的位置。但該位置的最大跳躍長度是 0 , 所以你永遠不可能到達最後乙個位置。
本題只需要返回乙個能或不能的結果,並不要求給出路徑,因此也暗示了動態規劃。通常解決並理解乙個動態規劃問題需要以下 步驟:
1.利用遞迴回溯解決問題
2.利用記憶表優化(自頂向下的動態規劃)
3.移除遞迴的部分(自底向上的動態規劃)
回溯法,其實是乙個模板
1, 以當前位置為源流往下摸排所有可以跳到的位置
2, 最終遞迴返回源流位置
3, 然後再以下面乙個位置作為源流位置,重複上述操作
回溯法的效率是很低的,以上已經說過了,本題只需要返回乙個能或不能的結果,並不要求給出路徑,因此回溯法的作用是讓我們理解這個過程,動態規劃法和回溯法的方向是相反的!!
class
solution
:# 回溯法複雜度o(2^n),可以理解成搜尋一顆樹的各個路徑,每個子節點上都有兩條路可以走
defcanjump
(self, nums: list[
int])-
>
bool
:return self.canjumpfromposition(nums,0)
defcanjumpfromposition
(self, nums, position)
:# 邊界
# 當前位置如果為終點,返回true
if position ==
len(nums)-1
:return
true
# 從當前位置能跳到的最遠位置
furtherestjump =
min(nums[position]
+position,
len(nums)-1
)# 從當前位置的下乙個位置開始摸排
for i in
range
(position+
1, furtherestjump+1)
:# 以此為源流往下摸排所有可以跳到的位置
# 最終遞迴返回當前位置,也就是源流
if self.canjumpfromposition(nums, i)
:return
true
# 然後再以下面乙個位置作為源流位置,重複上述操作
# 如果當前位置能跳到的範圍內都檢查過了,都不能到達終點,則說明當前位置不可能到達終點
return
false
自頂向下的動態規劃法,其實就是回溯法,只不過用了記憶表儲存中間結果,依然遞迴所以np難
class
solution
:# 回溯法複雜度o(2^n),可以理解成搜尋一顆樹的各個路徑,每個子節點上都有兩條路可以走
defcanjump
(self, nums: list[
int])-
>
bool
: n =
len(nums)
# 記錄對於每個位置,是否能跳到終點
mem =
[none]*n
mem[n-1]
=true
return self.canjumpfromposition(nums,
0, mem)
defcanjumpfromposition
(self, nums, position, mem)
:# 邊界
# 當前位置如果存在記憶,則直接返回記憶的內容
if mem[position]
!=none
:return mem[position]
# 從當前位置能跳到的最遠位置
furtherestjump =
min(nums[position]
+position,
len(nums)-1
)# 從當前位置的下乙個位置開始摸排,直到它能跳到的最遠位置
for i in
range
(position+
1, furtherestjump+1)
:# 以此為源流往下摸排所有可以跳到的位置
# 最終遞迴返回當前位置,也就是源流
if self.canjumpfromposition(nums, i)
: mem[position]
=true
return
true
# 然後再以下面乙個位置作為源流位置,重複上述操作
# 如果當前位置能跳到的範圍內都檢查過了,都不能到達終點,則說明當前位置不可能到達終點
mem[position]
=false
return
false
真正的動態規劃是和回溯法方向相反的
為了**狀態轉移方程,我們先在上面一種解法的基礎上,簡單反轉一下, o(n2)
值得注意的是,我們發現反轉後,對於每乙個位置,它右邊的位置都是有記憶內容存在的
public class
solution
{def
canjump
(self, nums: list[
int])-
>
bool
: n =
len(nums)
mem =
[none]*n
mem[n-1]
=true
# 從終點左邊一位開始,
for i in
range
(n-2,-
1,-1
):# 當前位置能跳到的最遠位置
furtherestjump =
min(nums[i]
+i, n-1)
# 在當前位置的跳動範圍內遍歷
for j in
range
(i+1
, furtherestjump+1)
:# 如果這個跳動範圍內存在能達終點的位置
if mem[j]
:# 則當前位置也是可以到達終點的
mem[i]
=true
break
# 如果跳動範圍內檢查後,沒有乙個能到終點的位置,則當前位置也無法到終點
mem[i]
=false
return mem[
0]
現在就容易得到我們的狀態轉移方程了,時間複雜度o(n)
class
solution
:# 最右邊的位置一定是可以到達終點的位置,它也是「目前最左邊的乙個可達終點的位置」
# 從終點左邊一位開始往前遍歷每個位置,判斷其是否可以達到「目前最左邊的乙個可達終點的位置」
# 如果可以的話,將這個位置記錄為「目前最左邊的乙個可達終點的位置」
# 狀態轉移方程:
# i + nums[i] >= leftmost: 對於任意位置 i,判斷它是否能到達「目前最左邊的乙個可達終點的位置」leftmost
defcanjump
(self, nums: list[
int])-
>
bool
: leftmost =
len(nums)-1
for i in
range
(leftmost,-1
,-1)
:if i + nums[i]
>= leftmost:
leftmost = i
return leftmost ==
0
作為參考,給出貪心法 o(n)
# 從左邊開始對於每個位置,記錄歷史上能達到的最遠位置,初始為0
defcanjump
(self, nums: list[
int])-
>
bool
: furtherestindex_inhistory =
0 n =
len(nums)
for i in
range
(n):
if i > furtherestindex_inhistory:
return
false
furtherestindex_inhistory =
max(i + nums[i]
, furtherestindex_inhistory)
if furtherestindex_inhistory >= n -1:
return
true
return furtherestindex_inhistory >= n -
1
LeetCode55 跳躍遊戲
leetcode55.跳躍遊戲 給定乙個非負整數陣列,你最初位於陣列的第乙個位置。陣列中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最後乙個位置。示例 1 輸入 2,3,1,1,4 輸出 true 解釋 從位置 0 到 1 跳 1 步,然後跳 3 步到達最後乙個位置。示例 2 輸入...
LeetCode 55 跳躍遊戲
題目鏈結 題目描述 給定乙個非負整數陣列,你最初位於陣列的第乙個位置。陣列中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最後乙個位置。示例輸入 2,3,1,1,4 輸出 true 解釋 從位置 0 到 1 跳 1 步,然後跳 3 步到達最後乙個位置。輸入 3,2,1,0,4 輸出 ...
LeetCode 55 跳躍遊戲
問題 給定乙個非負整數陣列,你最初位於陣列的第乙個位置,陣列中的每個元素代表你在該位置可以跳躍的最大長度,判斷你是否能夠到達最後乙個位置。示例 1 輸入 2,3,1,1,4 輸出 true 解釋 從位置 0 到 1 跳 1 步,然後跳 3 步到達最後乙個位置。示例 2 輸入 3,2,1,0,4 輸出...