Java資料結構與演算法--遞迴和回溯

微笑面對生活發表於2018-08-20

什麼是遞迴

任何呼叫自身的函式成為遞迴。遞迴是從跟數學領域借鑑過來的一種有用的技術,遞迴程式碼通常比迭代程式碼更加簡潔易懂。

例項

以階乘喂例,其遞迴定義如下:

n!=1          n=0

n!=n*(n-1)!     n>0
複製程式碼

程式碼實現:

    public static void main(String[] args) {
        int fact = Fact(5);
        System.out.println(fact);
    }
    static int Fact(int n){
        if (n==1){
            return 1;
        }
        if (n==0){
            return 1;
        }
        return n*Fact(n-1);
    }
複製程式碼

遞迴視覺化

每次遞迴呼叫都在記憶體中生成一個新的函式副本,一旦函式結束,這些副本就從記憶體中刪除。

Java資料結構與演算法--遞迴和回溯

遞迴與迭代

遞迴:

  • 當達到基本情形時,遞迴終止
  • 每次遞迴都需要額外的空間用於棧幀開銷
  • 如果無窮遞迴(沒有出口),程式會堆疊溢位
  • 某些問題用遞迴更容易解決

迭代:

  • 當迴圈條件為假時,迭代終止
  • 每次迭代不需要額外空間
  • 出現死迴圈後程式會一直迴圈執行
  • 求解問題可能沒有遞迴那樣顯而易見

遞迴演算法的經典用例

  • 斐波那契數列、階乘
  • 歸併排序、快速排序
  • 二分查詢
  • 樹的遍歷
  • 圖的遍歷:深度+廣度
  • 動態規劃
  • 分治演算法
  • 漢諾塔
  • 回溯演算法

相關問題

  1. 漢諾塔謎題 演算法:
  • 將源柱最上面的n-1個圓盤移動到輔助柱上
  • 將第n個圓盤從源柱移到目的柱。
  • 將輔助柱的n-1個圓盤一刀目的柱
  • 圓柱最上面的n-1個圓盤移動到輔助柱又可以看成一個新問題,然後以同樣的方式解決。
    /**
     * @param n 第幾個圓盤
     * @param frompeg 源柱
     * @param topeg 目標柱
     * @param auxpeg 輔助柱
     */
    void TowersOfHanoi(int n,char frompeg,char topeg,char auxpeg){
        /**
         * 如果只有一個圓盤,直接移動並返回
         */
        if (n == 1){
            System.out.println("move disk 1 from peg"+frompeg+"to peg"+topeg);
        }
        /**
         * 利用C柱做輔助,將A最上面的n-1個圓盤移動到B
         */
        TowersOfHanoi(n-1,frompeg,auxpeg,topeg);
        /**
         * 將剩下的圓盤從A移動到C
         */
        System.out.println("move disk from peg"+frompeg+"to peg"+topeg);
        /**
         * 利用A做輔助,將B上的n-1個圓盤移動到C
         */
        TowersOfHanoi(n-1,auxpeg,topeg,frompeg);
    }
複製程式碼
  1. 給定一個陣列,用遞迴方法判定陣列中的元素是否是有序的。
    int isArrayInSortedOrder(int[] a,int index){
        if (a.length == 1){
            return 1;
        }
        return a[index-1]<=a[index-2]?0:isArrayInSortedOrder(a,index-1);
    }
複製程式碼

什麼是回溯

回溯是一種採用分治策略進行窮舉搜尋的方法。

回溯經典用例

  • 二進位制串
  • 生成k進位制串
  • 揹包問題
  • 廣義字串
  • 哈密頓迴路
  • 圖著色問題

相關問題

  1. 生成所有n位長的字串。假設A[0..n-1]是一個大小為n的陣列。
    static void Binary(int n){
        if (n<1){
            Arrays.stream(A).forEach(s-> System.out.print(s+" "));
        }else {
            A[n - 1] = 0;
            Binary(n - 1);
            A[n - 1] = 1;
            Binary(n - 1);
        }
    }
複製程式碼

根據問題規模減小和遞迴求解主定理可以求得時間複雜度為2^n

相關文章