遞歸:方法內部調用方法本身的一種編程技巧。
當一個方法(功能)被重複使用時,如果每一次使用該方法參數都不確定,都是由上次的方法返回的結果來確定的,這個時候就要使用遞歸。
比如: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); } } }
關於漢諾塔,這裏再上傳一個ppt,幫助大家理解。