天天看點

java算法1_遞歸

遞歸:方法内部調用方法本身的一種程式設計技巧。

當一個方法(功能)被重複使用時,如果每一次使用該方法參數都不确定,都是由上次的方法傳回的結果來确定的,這個時候就要使用遞歸。

比如: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

可以看出,執行過程為:找的時候逐層向裡面找,執行的時候最裡面先執行然後外面依次執行,最後是最外面執行。

java算法1_遞歸

上圖簡單畫出了執行順序,和上面文字描述一緻。

遞歸另一個需要注意的問題是不要多(>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為例:

java算法1_遞歸

想得到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);
        }
    }
}