不知道大家發現沒有,執行遞迴演算法,特別是遞迴執行層數多的時候,結果極其的慢,而且遞迴層數達到一定的值,還可能出現記憶體溢位的情況。本文就要將為你解釋原因和對應的解決方案。
大家都知道遞迴的實現是通過呼叫函式本身,函式呼叫的時候,每次呼叫時要做位址儲存,引數傳遞等,這是通過乙個遞迴工作棧實現的。具體是每次呼叫函式本身要儲存的內容包括:區域性變數、形參、呼叫函式位址、返回值。那麼,如果遞迴呼叫n次,就要分配n區域性變數、n形參、n呼叫函式位址、n返回值,這勢必是影響效率的,同時,這也是記憶體溢位的原因,因為積累了大量的中間變數無法釋放。
遞迴與迴圈是兩種不同的解決問題的典型思路。當然也並不是說迴圈效率就一定比遞迴高,遞迴和迴圈是兩碼事,遞迴帶有棧操作,迴圈則不一定,兩個概念不是乙個層次,不同場景做不同的嘗試。
首先,看一下系統棧和使用者棧的用途。
優點:**簡潔、清晰,並且容易驗證正確性。(如果你真的理解了演算法的話,否則你更暈)
缺點:它的執行需要較多次數的函式呼叫,如果呼叫層數比較深,需要增加額外的堆疊處理(還有可能出現堆疊溢位的情況),比如引數傳遞需要壓棧等操作,會對執行效率有一定影響。但是,對於某些問題,如果不使用遞迴,那將是極端難看的**。
優點:速度快,結構簡單。
缺點:並不能解決所有的問題。有的問題適合使用遞迴而不是迴圈。如果使用迴圈並不困難的話,最好使用迴圈。
1) 一般遞迴呼叫可以處理的演算法,也可以通過迴圈去解決,常需要額外的低效處理。
2)現在的編譯器在優化後,對於多次呼叫的函式處理會有非常好的效率優化,效率未必低於迴圈。
是記憶體中屬於作業系統空間的一塊區域,其主要用途為:
1)儲存中斷現場,對於巢狀中斷,被中斷程式的現場資訊依次壓入系統棧,中斷返回時逆序彈出;
2)儲存作業系統子程式間相互呼叫的引數、返回值、返回點以及子程式(函式)的區域性變數。
是使用者程序空間中的一塊區域,用於儲存使用者程序的子程式間相互呼叫的引數、返回值、返回點以及子程式(函式)的區域性變數。
我們編寫的遞迴程式屬於使用者程式,因此使用的是使用者棧。
以上初略介紹了遞迴與迴圈的實現機理,似乎**簡潔和效率不能共存。那麼有沒有一種方法能擁有遞迴**簡潔的好處,同時給我們帶來更快的速率麼?演算法的世界會告訴你,一切皆有可能。它的名字叫做尾遞迴。
讓遞迴和尾遞迴來做乙個對比吧。
用線性遞迴實現fibonacci函式,程式如下所示:
int fibonaccirecursive(int n)
遞迴寫的**非常容易懂,完全是根據函式的條件進行選擇計算機步驟。例如現在要計算n=5時的值,遞迴呼叫過程如下圖所示,可以看出,程式向下遞迴,向上返回,所以每一步都需要儲存中間變數和過程。
顧名思義,尾遞迴就是從最後開始計算, 每遞迴一次就算出相應的結果, 也就是說, 函式呼叫出現在呼叫者函式的尾部, 因為是尾部, 所以根本沒有必要去儲存任何區域性變數。直接讓被呼叫的函式返回時越過呼叫者, 返回到呼叫者的呼叫者去。尾遞迴就是把當前的運算結果(或路徑)放在引數裡傳給下層函式,深層函式所面對的不是越來越簡單的問題,而是越來越複雜的問題,因為引數裡帶有前面若干步的運算路徑。
尾遞迴是極其重要的,不用尾遞迴,函式的堆疊耗用難以估量,需要儲存很多中間函式的堆疊。比如f(n, sum) = f(n-1) + value(n) + sum,會儲存n個函式呼叫堆疊,而使用尾遞迴f(n, sum) = f(n-1, sum+value(n)),這樣則只保留後乙個函式堆疊即可。
採用尾遞迴實現fibonacci函式,程式如下所示:
int fibonaccitailrecursive(int n,int ret1,int ret2)
例如現在要計算n=5時的值,尾遞迴呼叫過程如下圖所示:
從圖可以看出,尾遞迴不需要向上返回了,但是需要引入額外的兩個空間來保持當前的結果,這樣減少了中間變數的儲存和返回,大大提公升了效率,而且避免了記憶體溢位。
相信很多讀者對於快速排序都耳熟能詳,不知道各位還記得快速排序的實現就是基於遞迴實現的麼,於是這裡就提供了一種優化快速排序的方案,當然尾遞迴不能改變快速排序的時間複雜度,但是提公升效能還是沒問題的。筆者不再做詳細介紹,只貼上實現**,留給各位獨立思考的空間。
int partition(int *p,int len,int start,int last)
*(p+i)=flag;
return i;
}
void quicksort(int *p,int len,int start,int last)
}
Logstash為什麼那麼慢? json序列化
今天跟峽谷金橋聊天,詢問起logstash的效能,金橋提示說logstash中json的序列化是浪費效能的一方面。於是便有了下面的測試 首先需要造乙份資料,資料可以通過logstash的generator來造。input output 生成的資料格式如下 測試的思路是,從test.log檔案中讀取資...
遞迴演算法,什麼叫遞迴?
這個是求階乘的遞迴 很經典的,很多書中都會有的!include stdio.h int fun int n main 程式呼叫自身的程式設計技巧稱為遞迴 乙個過程或函式在其定義或說明中有直接或間接呼叫自身的一種方法,它通常把乙個大型複雜的問題層層轉化為乙個與原問題相似的規模較小的問題來求解,遞迴策略...
fibnacci數列遞迴演算法及改進
fibnacci數列1,1,2,3,5,8,13,21.數列第一項和第二項等於1,從第三項開始開始,每一項都等於前兩項之和,簡單的用數學公式表示 n 0或n 1時,f n 1 n 1時,f n f n 1 f n 2 可以看出使用遞迴演算法可解決該問題,python 如下 def fibnacci ...