一枚菜鳥的leetcode刷題筆記 Day8

2021-10-16 16:56:54 字數 4024 閱讀 6885

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...