遞迴

就是指函式自己呼叫自己,但遞迴會增加程式在執行時的負擔,資料太多時記憶體甚至會不夠。 但有些問題用遞迴來解決,可以增加效益,這就要自行做判斷取捨。

為什麼遞迴執行時會效能比較不好呢?因為每次在函式中呼叫函式時,上一層的函式就進行到一半,在stack記憶體中佔據一個空間後,等待下一層的函式執行完畢回報,於是這樣層層堆積,如果遞迴的次數太多,可能就會發生溢位的狀況,所以遞迴要有停止機制(Base Case)。

 

費氏數列

遞迴的經典例子之一:費氏數列,(1.1.2.3.5.8.13.21......),第0項為0,第一項為1,後一項為前兩項的和

寫成數學公式為

  • (n≧2)

用迴圈寫的話,第一種方法:陣列紀錄數列的每一項數值

優點:邏輯比較簡單好懂

缺點:因為記錄每一項,記憶體佔用空間較多

public static void main(String[] args) {
    //用迴圈寫費氏
    int fi[] = new int[5];
    for (int i = 0; i < fi.length; i++) {
        if (i == 0 || i == 1) {    
            fi[i] = 1;
            continue;
        }
        fi[i] = fi[i - 1] + fi[i - 2];
    }

    for (int i = 0; i < fi.length; i++) {
        System.out.print(fi[i] + " ");
    }

}

 

迴圈的第二種方法:設定變數a和b,一個存b入前兩項的和

圖解,就是一個變數a,b一直向右推移的過程,先將原ab和,放入tmp變數,a放入b向右推一格,b再放入tmp(a+b),向右推一格.......

 

這邊設定 i 從3開始,是自己希望 i 等於幾 就代表b現在是數列中的第幾個數

image

public static void main(String[] args) {
    //用迴圈寫費氏,得出第i個數值
    int a = 1;
    int b = 1;
    int index = 10;  //求第10項的數值
    for (int i = 3; i < index + 1; i++) {
        int tmp = a + b;
        a = b;
        b = tmp;
    }
    System.out.print(b);


}

 

再來是用遞迴寫費氏的函式,也很單純,將數學公式帶入即可

//費氏數列第幾個
public static int fiIndex(int x) {
    if(x==1||x==0){   //base case
        return  x;
    }
    return fiIndex(x-1) + fiIndex(x-2);
}

 

將數列列出的函氏

//費氏數列列出
public static int[] fi(int x) {
    int[] fi = new int[x];
    for (int i = 0; i < fi.length; i++) {
        int index = i + 1;
        fi[i] = fiIndex(index);
    }
    return fi;
}

 

 

河內塔

這個題目也很經典,看老師的教學我能理解code的概念,但想不通他是怎麼透過這樣簡單的三行兩遞迴,算出這麼多步驟的。實際上運行的詳細步驟真的想了解啊.....

河內塔規則:河內塔維基百科

基礎概念步驟就是:

1.先將除了底盤以外的盤子 移到中間

2.再將最底部的盤子 移到最右邊

3.最後將中間的全部盤子 移到最右邊

 

重點是,當一根住子上的盤子數量不是1時,就必須重複上述的步驟

//河內塔
public static void hanoi(int x, int a, int b, int c) {    //(盤子數量,柱子代號a,柱子代號b,柱子代號c) 
    if (x == 1) {    //函式設定當盤子的數量為1的時候,移動參數a到參數c
        System.out.println(a + "->" + c);
        return;
    }
    hanoi(x - 1, a, c, b);  //當盤子數量不是1的時候,移動除底盤以外的盤子 從a到b
    System.out.println(a + "->" + c);  //再將剩餘的底盤 從a移到c
    hanoi(x - 1, b, a, c);   //把移到b的其他底盤,從b移到c
}

這邊常看到的疑問有

Q 為什麼這樣就能確保大盤子不會疊在小盤子上呢?

A 因為依據上面的步驟,只要盤子上面有疊盤子,一律是將底部以外的盤子 先移到輔助的柱子上,再去移動剩餘的盤子,一直重複呼叫自身函式,直到盤子數量為1。

看個動畫會更清楚 來源: https://www.bilibili.com/video/BV1Zh411y7XB?share_source=copy_web

自己也畫了步驟演示,再遞迴的過程中,會不斷的替換三根柱子的位子,維持a>c的動作

image


image


image

 

有一個影片也說明得很詳細,推薦:https://www.bilibili.com/video/BV1HE411W71E?share_source=copy_web

 

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 Lillian 的頭像
    Lillian

    安安的code日記

    Lillian 發表在 痞客邦 留言(0) 人氣()