遞歸在處理某些問題,尤其在循環遍歷方面,常常可以用比較簡潔的代碼實現一些複雜的功能;但是由於它是調用自身,所以如果層級比較多,一是性能會比較差,其次容易出現棧溢出;
對於這兩點通過添加一個變量存儲中間結果可以部分提升性能,但是還是有上限,尤其是調用萬級的次數,還是有棧溢出的風險;一個例子:關於斐波那契數列的值的獲取;
1 普通遞歸:
var count=0;
//斐波那契數列
var recurFib=function(n){
count++;
if(n<2){
return n;
}else{
return recurFib(n-1)+recurFib(n-2);
}
}
當n爲10的時候,大概需要執行177次,其中100多次的調用是重複的;當n爲1000的時候,基本上就沒法用了。
2 遞歸+記憶數組;
var countMe=0;
var recurfibMem=function(){
var memo=[0,1];//保存中間值
var fib=function(n){
countMe++;
var result=memo[n];
if(typeof result !='number'){
result=fib(n-1)+fib(n-2);
memo[n]=result;
}
return result;
}
return fib;
}()
當n爲10的時候,大概需要執行19次,相比較原始的方法,少調用了很多次。當n=10000的時候,耗時在幾毫秒之間,也還不錯。但是當n的值在往上加,就可能會出現棧溢出的情形;
3 動態規劃:動態規劃從思路上講和添加記憶數組差不多。區別在於數組的值不是通過遞歸獲取的,而是通過一步步的“平行”計算獲取的,這樣就不會出現調用過深的情況;
//動態規劃
var countD=0;
var recurFibDyn=function(n){
var last=1;
var nextLast=1;
var result=1;
for(var i=2;i<n;i++){
countD++;
countD++;
result=last+nextLast;
nextLast=last;
last=result;
}
return result;
}
測試結果:當n=10000的時候,耗時在2-3毫秒。當n=10w,n=100w,依然可以正常運行且時間沒有指數級別的增加。當然在這裏的話返回的值已經超出了js默認數字的最大值。