問:何爲遞歸函數?
說人話:本身調用本身的函數就叫遞歸函數。java
實現一個遞歸函數,我將其歸納爲是一個「推卸責任」的過程,分爲3個步驟:算法
咱們將用求階乘的例子來詳細解釋這3個步驟。數組
首先,明確輸入輸出:咱們但願只要給該方法/函數一個整數,就能返回它的階乘。那麼該遞歸方法的方法簽名即爲以下:數據結構
public int factorial(int n){};
這樣咱們就完成了實現遞歸函數的第1個步驟了。函數
很顯然階乘的邊界狀況就是求1的階乘,它沒有理由再把計算的責任推卸給任何人了,由於它本身就能直接得出計算結果1,因此求1的階乘就是咱們要找的那隻「替罪羊」。spa
所以,基準狀況即爲以下:code
if (n == 1) return n;
提早找好了「替罪羊」,接下來咱們只須要把「責任推卸鏈」完成,讓責任一級一級最終推卸到替罪羊身上,就能夠大功告成!遞歸
而要完成這條「責任推卸鏈」,其實就是列出關於原問題和子問題的數學等價關係式。ci
對於求階乘而言,原問題和子問題的等價關係式爲:\(n! = n \times (n - 1)!\)。這樣,就將問題從第\(n\)層成功地轉移到了第\(n-1\)層。get
那麼對應的代碼實現即爲以下:
else return n * factorial(n - 1);
綜合以上3個步驟,完整版的實現階乘的遞歸函數即爲:
/** Returns n factorial. */ public int factorial(int n) { // Base case if (n == 1) return n; // Recursive case else return n * factorial(n - 1); }
在這個遞歸函數裏,咱們定義了遞歸狀況來一步步簡化問題,也定義邊界狀況來計算出最終的邊界值。因此咱們大可放心地調用該函數,讓它自行去解決問題。
看到這裏,相信你已經掌握了遞歸函數的寫法。能夠看出,寫出一個遞歸函數並無那麼容易。而咱們可不但願當咱們絞盡腦汁憋出一個遞歸函數後,卻換來老闆一句「畫蛇添足,淨瞎折騰」。
那要避免這種狀況,咱們得先弄清楚一個前提:何時派上遞歸最爲合適?
咱們先來談談遞歸的缺點:
n = 5
時,須要計算一遍的fibonacci(3)
,推導到n = 4
時,又須要計算一遍fibonacci(3)
。也就是說求一個5的階乘須要計算兩遍的fibonacci(3)
,因此說時間效率低。而遞歸主要是用於如下兩種狀況:
因此,除非是以上兩種狀況,不然儘可能避免使用遞歸,以免對空間和時間產生大的消耗。(P.S. 把浪費的這些功夫拿去打王者榮耀它不香嗎...)
遞歸函數是本身調用本身的函數。經過列出方法簽名,完成基準狀況和遞歸狀況便可完成一個遞歸函數。可是用遞歸實現一般會對時間和空間產生大的消耗,所以除非數據結構自己就是按遞歸的形式定義或是問題能以一樣的解法逐級減少問題規模,不然應儘可能避免使用遞歸。
創做不易,點個贊再走叭~