70 - 爬樓梯
78 - 子集
乙個機械人位於乙個 m x n 網格的左上角 (起始點在下圖中標記為 「start」 )。機械人每次只能向下或者向右移動一步。機械人試圖達到網格的右下角(在下圖中標記為 「finish」 )。問總共有多少條不同的路徑?
class
solution
:def
uniquepaths
(self, m:
int, n:
int)
->
int:
if n ==
1or m ==1:
return
1return self.uniquepaths(m-
1, n)
+ self.uniquepaths(m, n-
1)
很不幸超時了,超時樣例是m=23,n=12.
遞迴的思路很簡單,機械人從(1,1)走到(m,n)的不同路徑數 = 機械人從(1,1)走到(m-1,n)的不同路徑數 + 機械人從(1,1)走到(m,n-1)的不同路徑數。而遞迴的base情況就是 m=1 或者 n=1. 因為此時只需要一直向右走或者一直向下走就可以了,都是只有一種情況,所以返回 1.
class
solution
:def
uniquepaths
(self, m:
int, n:
int)
->
int:
#維護乙個**table,table的第(i,j)個位置記錄了機械人走到當前位置的不同路徑數
table =[[
0for _ in
range
(n)]
for _ in
range
(m)]
#先把第一行和第一列填好,因為這些位置的路徑數都是1
for i in
range
(m):
table[i][0
]=1for j in
range(1
, n)
: table[0]
[j]=
1#開始填**
for i in
range(1
, m)
:for j in
range(1
, n)
: table[i]
[j]= table[i-1]
[j]+ table[i]
[j-1
]return table[m-1]
[n-1
]
動態規劃其實和遞迴的想法是一樣的,核心點都在於想清楚:機械人從(1,1)走到(m,n)的不同路徑數 = 機械人從(1,1)走到(m-1,n)的不同路徑數 + 機械人從(1,1)走到(m,n-1)的不同路徑數。這就是狀態轉移方程:table[i][j] = table[i-1][j] + table[i][j-1]. 不同在於遞迴是倒著往回推,動態規劃是從初始情況往後推。注意 table[i-1][j] 和 table[i][j-1] 都要在 table[i][j] 前被遍歷到。
假設你正在爬樓梯。需要 n 階你才能到達樓頂。每次你可以爬 1 或 2 個台階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是乙個正整數。
class
solution
:def
climbstairs
(self, n:
int)
->
int:
if n ==1:
return
1if n ==2:
return
2return self.climbstairs(n-1)
+ self.climbstairs(n-
2)
因為一次可以走1或2步,因此 n級台階的走法 = n-1級台階的走法(最後走一步) + n-2級台階的走法(最後走兩步)
遞迴超時了。因為遞迴比迴圈慢很多,下面改寫成迴圈的解法。
class
solution
:def
climbstairs
(self, n:
int)
->
int:
s1 =
1 s2 =
2for i in
range(3
, n+1)
:if s1 < s2:
s1 = s1 + s2
else
: s2 = s1 + s2
if n ==1:
return
1elif n ==2:
return
2else
:return
max(s1, s2)
注意返回時分情況討論。
給你乙個整數陣列 nums ,返回該陣列所有可能的子集(冪集)。解集不能包含重複的子集。
示例一:
輸入:nums = [1,2,3]
輸出:[,[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例二:
輸入:nums = [0]
輸出:[,[0]]
分析:這一題和全排列不同,陣列元素的順序不能換,不然就會導致重複的情況。
本題解法參考了簡單問題細緻分析,兩種dfs回溯思路
class
solution
:def
subsets
(self, nums: list[
int])-
> list[list[
int]]:
ans =
defbacktrack
(index, tmp)
:for i in
range
(index,
len(nums)):
backtrack(i+
1, tmp +
[nums[i]])
backtrack(0,
)return ans
這個遞迴比較有意思的是,沒有乙個顯式定義的出口,而是通過迴圈控制遞迴結束。即 for 迴圈完成,當前層次的遞迴就結束了。回溯還是通過遞迴完成的,因為遞迴完成一次相當於入棧出棧一次,出棧即回溯了。
那麼此處的遞迴函式backtrack到底是什麼意思呢,它實現了以當前index為開頭的所有情況,即陣列 nums[index, len(nums)] 的所有子集。
以[1,2,3]為例,此解法元素的新增順序是:, [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]
class
solution
:def
subsets
(self, nums: list[
int])-
> list[list[
int]]:
ans =
defbacktrack
(index, tmp)
:if index ==
len(nums)
:#定義了遞迴的base情況,當nums中的所有數字都被考慮過一遍時,return
#子集中選擇當前nums[index]
backtrack(index+
1, tmp +
[nums[index]])
#子集中不選擇當前nums[index]
backtrack(index+
1, tmp)
backtrack(0,
)return ans
想法非常簡單,對於每個元素都考慮選進子集和不選進子集兩種情況,一次遍歷陣列中的所有元素,就會形成一棵樹,最終的所有情況是樹的所有葉子結點。我感覺這個解法裡回溯的意味很淺,主要是遞迴,不過遞迴本質上就是回溯了。
以[1,2,3]為例,此解法元素的新增順序是:[1,2,3], [1,2], [1,3], [1], [2,3], [2], [3],
一枚菜鳥的leetcode刷題筆記 Day5
給你乙個鍊錶陣列,每個鍊錶都已經按公升序排列。請你將所有鍊錶合併到乙個公升序鍊錶中,返回合併後的鍊錶。class solution def merge2lists self,l1,l2 l1 and l2 are two linked lists head listnode rehead head ...
一枚菜鳥的leetcode刷題筆記 Day11
給定乙個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。說明 你的演算法應該具有線性時間複雜度。你可以不使用額外空間來實現嗎?class solution def singlenumber self,nums list int int tmp for n...
一枚菜鳥的leetcode刷題筆記 Day14
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。class solution def findkthlargest self,nums list int k int int nums.sort 從小到大排列 return n...