前段時間一直寫了幾個算法題目,發現有個很牛逼的算法,動態規劃,雖然有的解題思路和動態規劃很像,可是當時不知道其中的原理和一些通用性,接下來的幾天,經過一些栗子一點一點揭開動態規劃那神祕的面霜,我也是現學現賣的,若是有那裏寫錯的歡迎給我留言指正。javascript
動態規劃有時被稱爲遞歸的相反的技術。遞歸是從頂部開始將問題分解,經過解決全部分解小問題的方式,來解決整個問題。而動態規劃這是從底部開始解決問題,將全部小問題解決掉,而後合併成總體的解決方案,從而解決掉整個大問題。遞歸方式雖然很簡潔,可是效率不高,可是不能說遞歸是很差的,本質上是,命令式語言和麪向對象的語言對遞歸的實現不夠完善,由於它們沒有將遞歸做爲高級編程特性。java
動態規劃方案一般使用一個數組來創建一張表,用於存放被分解成衆多子問題的解。當算法執行完畢,最終的解法將會在這個表中找到。算法
今天咱們先從咱們最熟的斐波那契數列數列開始。編程
0, 1, 1, 2, 3, 5, 8, 13, 21, 24, 55, ...
從數列中能夠發現從第三個數開始的值是前兩個值的和。數組
遞歸解法
function fib(n){ if(n < 2){ return n; }else{ return fib(n - 1) + fib(n - 2); } } console.log(fib(10)); // 55
動態規劃解法
function fibDyn(n){ var temp = []; for(var i = 0; i <= n; i++){ temp[i] = 0 } if(n == 1 || n == 2){ return 1; }else{ temp[1] = 1; temp[2] = 2; for(var i = 3; i < n; i++){ temp[i] = temp[i - 1] + temp[i -2]; } return temp[i - 1]; } } fibDyn(10) // 55
從程序中咱們能夠看出,初始化了一個和傳入等長的空數組,去存放每次運算厚的結果。微信
測試程序運行時間
var start = new Date().getTime(); console.log(fib(10)) var stop = new Date().getTime(); console.log('遞歸消耗' + (stop - start) + '毫秒'); start = new Date().getTime(); console.log(fibDyn(10)) stop = new Date().getTime(); console.log('動態規劃消耗' + (stop - start) + '毫秒');
運行結果測試
55 遞歸消耗-- 0 毫秒 55 動態規劃消耗-- 0 毫秒
fib(20)優化
6765 遞歸消耗-- 1 毫秒 6765 動態規劃消耗-- 0 毫秒
fib(30)spa
832040 遞歸消耗-- 17 毫秒 832040 動態規劃消耗-- 0 毫秒
改變fib(n)中的 n 的值咱們會發現,動態規劃的解決方案姚比遞歸解決方案高效不少。code
優化斐波那契數列的動態規劃算法
咱們看到這個動態規劃的算法是要了一個空數組,這是你可能已經想到使用迭代的方案計算,能夠不使用數組,須要用到的素組的緣由事由於動態規劃算法一般須要吧中間的結果保存起來。一下是優化的迭代版。
function fibDyn(n){ var prev = 1; var middle = 1; var result = 1; for(var i = 2; i < n; i++){ result = prev + middle; prev = middle; middle = result; } return result; } fibDyn(10) // 55
這時候咱們能夠看到少了建立數組這一步,效率沒變,可是空間複雜度變小了。
斐波那契數列在不少地方都會用上,我是從一個臺階問題發現的,同時知道了動態規劃的解法。有興趣的能夠在公衆號中回去「臺階問題」
歡迎關注微信公衆帳號查看最新文章