簡單來說,遞迴就是自己呼叫自己,在每次呼叫時傳入不同的變數。遞迴有助於解決複雜的問題,同時讓程式碼變得簡潔。
在之前的文章中,對遞迴有過簡單的介紹,現在進一步瞭解下遞迴的呼叫機制。
一、遞迴的呼叫機制
先上一段簡單的遞迴呼叫的程式碼:
package recursion;
public class RecursionTest {
public static void main(String[] args) {
test(4);
}
public static void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
}
可以看到,在main方法裡,執行test(4)
,當滿足n>2
的條件時,test()
會繼續呼叫test()
,直到不滿足遞迴條件,列印出n
的值。
執行結果其實也很容易想到:
n=2
n=3
n=4
Process finished with exit code 0
執行結果倒不是重點了,現在藉著這段程式碼再加張圖,看下遞迴的呼叫機制。
圖中所示就是在執行程式碼的過程中,jvm中發生的一些事情。不過這裡宣告一下,關於jvm的某些描述可能並不是很準確,這裡只是輔助理解記憶。
- 首先,在執行
main
方法時,會在棧裡開闢一個main方法的棧幀。
當main方法裡呼叫test
方法時,又會壓入一個棧幀,也就是入棧。當方法沒執行結束時,是不會出棧的。
所以,執行test(4)
,會繼續壓入一個棧幀(紅色箭頭)。 - 在
test(4)
裡,經過判斷會繼續呼叫test(3)
,於是繼續壓入一個棧幀。 - 在
test(3)
裡,經過判斷會繼續呼叫test(2)
,於是繼續壓入一個棧幀。 - 在
test(2)
裡,經過判斷,不再遞迴,於是執行了print程式碼,列印出n
的值為2。
方法執行完了就會出棧(黃色箭頭),回到test(3)
。 test(3)
列印出n的值為3
,繼續出棧,回到test(4)
。test(4)
列印出n的值為4,main
方法執行結束,退出程式。
所以,程式碼執行的結果就是2,3,4
。
二、使用遞迴需要知道的點
- 執行一個方法時,會建立一個新的受保護的獨立空間。比如上面的棧幀。
- 方法的區域性變數是獨立的,不會相互影響。比如上面每次遞迴時候的變數
n
。
但是,如果方法中使用的是引用型別變數,那會共享該引用型別。比如,引用一個陣列。 - 重點:遞迴必須向退出遞迴的條件逼近,否則就無限遞迴,最終棧溢位
StackOverflowError
。 - 當方法執行完畢,或者遇到
return
,就會返回。遵守誰呼叫,就將結果返回給誰。
比如上圖中最上面的棧幀test(2)
執行結束後,就返回到呼叫它的test(3)
。