LeetCode總結 -- 一維動態規劃篇
這篇文章的主題是動態規劃, 主要介紹LeetCode中一維動態規劃的題目, 列表如下:
Best Time to Buy and Sell Stock
在介紹上述具體題目之前, 我們先說說動態規劃的通常思路。 動態規劃是一種演算法思路(注意這裡不要和遞迴混淆, 事實上遞迴和迭代只是兩種不同的實現方法, 並不是演算法), 用一句話來總結就是, 動態規劃是利用儲存歷史資訊使得未來需要歷史資訊時不需要重新計 算, 從而達到降低時間複雜度, 用空間複雜度換取時間複雜度目的的方法。 我個人喜歡把動態規劃分為以下幾步:
1) 確定遞推量。 這一步需要確定遞推過程中要保留的歷史資訊數量和具體含義, 同時也會定下動態規劃的維度;
2) 推導遞推式。 根據確定的遞推量, 得到如何利用儲存的歷史資訊在有效時間(通常是常量或者線性時間)內得到當前的資訊結果;
3) 計算初始條件。 有了遞推式之後, 我們只需要計算初始條件, 就可以根據遞推式得到我們想要的結果了。 通常初始條件都是比較簡單的情況, 一般來說直接賦值即可;
4) (可選)考慮儲存歷史資訊的空間維度。 這一步是基於對演算法優化的考慮, 一般來說幾維動態規劃我們就用幾維的儲存空間是肯定可以實現的。 但是有時我們對於歷史資訊的要求不高, 比如這一步只需要用到上一步的歷史資訊, 而不需要更早的了, 那麼我們可以只儲存每一步的歷史資訊, 每步覆蓋上一步的資訊, 這樣便可以少一維的儲存空間, 從而優化演算法的空間複雜度。
動態規劃的時間複雜度是O((維度)×(每步獲取當前值所用的時間複雜度))。 基本上按照上面的思路, 動態規劃的題目都可以解決, 不過最難的一般是在確定遞推量, 一個好的遞推量可以使得動態規劃的時間複雜度儘量低。
接下來我們來看看具體題目, 一維動態規劃的題目主要分成兩類:
(1) 第一種是比較簡單的, 直接地按照上面步驟就可以解出來的, 確定遞迴量, 然後按遞迴式迭代就可以得到。 這種型別的題目是:
Climbing Stairs中遞推量很清晰, 就是爬到i級樓梯有多少種可行爬法。 而對於遞推式我們可以看出, 要到達i級樓梯, 必須通過i-1級或者i-2級(以為只能爬一級或者兩級), 如此可以得到到達i級樓梯的方式有f(i)=f(i-1)+f(i-2)種, 這樣遞推式也就出來了。 而初始條件則是一級樓梯是一種解法, 兩級樓梯是兩種解法(2或者11)。 有了這些接下來遞推到n級樓梯返回即可, 空間複雜度是O(n)(一維動態規劃乘以每一步的常量操作)。 空間上我們發現每一步,只需要前兩步的歷史資訊, 所以我們不需要儲存所有歷史資訊, 只需要儲存前兩步, 然後迭代替換就可以了, 所以空間複雜度是O(2)=O(1), 這裡對應於上面的第四步。
Unique Binary Search Trees思路還是類似的, 遞推式是稍有不同, 按左右子樹劃分然後進行累加, 最後歸結為
(2) 接下來我們介紹第二種型別, 雖然也是一維動態規劃, 但是區別在於這類題目需要維護兩個遞推量, 所以思路上有一點技巧。 不過還是比較有通法的, 我通常把這種方法稱為”區域性最優和全域性最優解法“。 這種方法中我們通常維護兩個量, 一個是到目前為止最好的結果資訊(全域性最優), 另一個必須包含新加進來的元素的最好的結果資訊(區域性最優), 然後還是推導遞推式, 計算初始條件, 跟動態規劃的通常思路一樣了。 Maximum Subarray和Best Time to Buy and Sell Stock就是這種型別的題目。
Maximum Subarray中對於遞推量我們維護兩個,一個是到目前為止最好的子陣列, 而另一個量則是加入當前元素之後, 包含當前元素的最好的子陣列, 最終我們是看全域性最優的變數的最優值, 而區域性最優卻是我們在遞推過程中維護全域性最優所需要的。 遞推式還是有點技巧, 第i+1步表示式如下:
local[i+1]=Math.max(A[i], local[i]+A[i]),就是區域性最優是一定要包含當前元素,所以不然就是上 一步的區域性最優local[i]+當前元素A[i](因為local[i]一定包含第i個元素,所以不違反條件),但是如果local[i]是負的,那麼加上他就不如不需要的,所以不然就是直接用A[i];
global[i+1]=Math(local[i+1],global[i]),有了當前一步的區域性最優,那麼全域性最優就是當前的區域性最優或者還是原來的全域性最優(所有情況都會被涵蓋進來,因為最優的解如果不包含當前元素,那麼前面會被維護在全域性最優裡面,如果包含當前元素,那麼就是這個區域性最優)。
初始條件都是0或者第一個元素既可以了, 一遍掃過來, 每次迭代更新兩個量(都是常量時間), 所以時間是O(n)。 空間上可以看出只需要上一步的資訊, 所以只需要儲存上一步的全域性最優和區域性最優即可, 複雜度是O(2) = O(1)。
Maximum Product Subarray的題目模型跟Maximum Subarray比較類似,只是把加法改成了乘法,思路還是用這個方法,只是注意這裡兩個負數相乘可能得到更優的乘法結果,所以我們在維護區域性最優時把區域性的最小值也存下來,這樣遇到負數時就可以得到也許更大的乘積。其他就跟Maximum Subarray是一致的了。
Best Time to Buy and Sell Stock跟Maximum Subarray是完全一樣的, 也是維護兩個量, 一個是到目前為止最好的交易(全域性最優), 另一個是在當前一天賣出的最佳交易(區域性最優), 其他步驟也是一樣的, 這裡就不列出來了。
可以看出, 上面五道一維動態規劃的題目都是按照我前面列出的四個步驟進行求解的, 事實上所有動態規劃題目都是按照這個基本思路來的。 掌握了套路之後就是看對具體問題要維護的遞推量的選擇了,這個個人感覺還是比較靠經驗的, 熟能生巧。 --------------------- 本文來自 Code_Ganker 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/linhuanmars/article/details/38468361?utm_source=copy