遞歸:方法内部調用方法本身的一種程式設計技巧。
當一個方法(功能)被重複使用時,如果每一次使用該方法參數都不确定,都是由上次的方法傳回的結果來确定的,這個時候就要使用遞歸。
比如:N 的階乘:
package edu.aiai.coll;
import java.util.Scanner;
public class TestRec {
public static void main(String[] args) {
System.out.println(fac((new Scanner(System.in)).nextInt()));
}
public static int fac(int n){
if(n==1){
return 1;
}else{
return n*fac(n-1);
}
}
}
注意點:
1遞歸必須有明确的出口(如上面代碼中的n等于1時的傳回1),不然就會一直遞歸下去,直到程式報StackOverflowError錯誤而終止。
2遞歸調用中各層的執行順序問題:最外層調用内層調用最内層,等最内層執行完畢之後,内層執行,然後最外層執行并傳回結果,以上面為例:如果計算4的階乘,是先算3的階乘,要想算3的階乘,先算2的階乘,想算2的階乘,先算1的階乘,1的階乘傳回1,fac(2)拿到1之後執行2乘以1,傳回2,2的階乘計算完畢,然後是3的階乘,最後是4的階乘,傳回結果,即為我們想要的結果。
按照上面說的執行過程來看一個檔案遞歸删除的例子:
package edu.aiai.coll;
import java.io.File;
public class TestRec {
public static void main(String[] args) {
File dir = new File("d:/test");
delDir(dir);
}
public static void delDir(File dir){
File[] arrFiles = dir.listFiles();
for(int i=0;i<arrFiles.length;i++){
if(arrFiles[i].isDirectory()){
delDir(arrFiles[i]);
}
arrFiles[i].delete();
System.out.println(arrFiles[i]);
}
dir.delete();
}
}
輸出結果為
d:\test\java\javaSE\BubbleTest.class d:\test\java\javaSE d:\test\java\NewFile.xml d:\test\java\question\api\allclasses-frame.html d:\test\java\question\api\resources\inherit.gif d:\test\java\question\api\resources d:\test\java\question\api d:\test\java\question\Num01.class d:\test\java\question d:\test\java |
可以看出,執行過程為:找的時候逐層向裡面找,執行的時候最裡面先執行然後外面依次執行,最後是最外面執行。

上圖簡單畫出了執行順序,和上面文字描述一緻。
遞歸另一個需要注意的問題是不要多(>1)處遞歸,這樣會驗證降低效率:
比如菲波那契數列:
package edu.aiai.coll;
public class TestRec {
public static void main(String[] args) {
System.out.println(f(10));
}
public static int f(int n){
if(n==1 || n==2){
return 1;
}
return f(n-1)+f(n-2);
}
}
效率低下原因如下圖:以9為例:
想得到f(9)的值,就得先算f(8)和f(7),想得到f(8)的值,就得先算f(7)和f(6),依次類推,運算的次數呈幾何級數增長,是以如果數大的話,計算效率會非常低。
這個我們可以通過循環疊加的方法解決:
long n1=1, n2=1;
for(int i=3; i<=50; i++){
n2 = n1+n2;
n1 = n2-n1;
}
再說一個著名的遞歸調用的問題:漢諾塔 Towers of Hanoi
package com.anjoyo.hanoi;
import java.util.Scanner;
public class TestHanoi {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("請輸入盤子數量:");
int niNum = scan.nextInt();
move(niNum,'A','B','C');
}
public static void move(int n,char a,char b,char c){
if(n==1){
System.out.println("盤 " + n + " 由 " + a + " 移至 " + c);
}else{
move(n - 1, a, c, b);
System.out.println("盤 " + n + " 由 " + a + " 移至 " + c);
move(n - 1, b, a, c);
}
}
}