淺談遞歸算法


1 引言

程序調用自身的編程技巧稱為遞歸( recursion)。遞歸作為一種算法在程序設計語言中廣泛應用。一個方法或函數在其定義或說明中有直接或間接調用自身的一種方法,它通常把一個大型復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需要少量的程序就可以描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量,讓程序更加的簡潔。

例如求和問題:若要求解S100 = 1 + 2 + 3 + 4 + …. + 100的值,通過循環的方式代碼如下:

int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum = sum + i;
}

通過遞歸方式是如何求解呢?由 **1 + 2 + 3 + 4 + …. + 100 **可以分解為 ( 1 + 2 + 3 + 4 + …. + 99) + 100,可以看出
  S100 = S99 + 100,可以得出 Sn = Sn-1 + n。通過遞歸的方式代碼如下:

public int sum(int n) {
    if (n == 1) {
        return 1;
    } else {
         return sum(n - 1) + n;
    }
}

通過遞歸代碼可以看出,sum() 方法中又調用了其自身,只是將傳入的參數發生改變。這種程序調用自身的方式就是遞歸

2 應用場景

​ 什么樣的問題才可以使用遞歸的方式求解呢?構成遞歸需要具備兩個條件:
  (1)子問題與原始問題屬於同樣的事情,二者的求解方法是相同的,且子問題比原始問題更易求解。
  (2)遞歸不能無限制地調用本身,必須有個遞歸出口。遞歸出口對應的情形相對簡單,可以化簡為非遞歸狀況處理。

3 實例

3.1 斐波那契數列

斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:
  1、1、2、3、5、8、13、21、34、……
  在數學上,斐波納契數列以如下被以遞推的方法定義:
  F(1)=1,F(2)=1,,F(n) = F(n-1) + F(n-2)(n>=3,n∈N*)

問題分析:
  斐波那契數列的對於原問題F(n)的求解可以轉為對F(n-1)、F(n-2)兩個子問題的求解,故符合條件(1)。由F(1)=1,F(2)=1,可以得出斐波那契數列問題是有遞歸出口的,遞歸出口對應F(1) = 1,F(2) = 1。求解斐波那契數列的代碼如下:

public class FibonacciSequence {
    public static void main(String[] args){
        System.out.println(Fribonacci(9));
    }
    
    public static int Fribonacci(int n){
        if(n <= 2)
            return 1;
        else
            return Fribonacci(n-1)+Fribonacci(n-2);
    }
}

3.2 階乘問題

階乘問題的數學表達式為:n! = n * (n-1) * (n-2) * …* 1 (n>0)。通過分析可以得出n! = (n-1)! * n。令F(n) = n!,則F(n) = F(n-1) * n。則階乘問題符合條件(1)。由0! = 1,可以得出F(0) = 1。則階乘問題符合條件(2),遞歸出口為F(0) = 1。利用遞歸求解階乘問題代碼如下:

int factorial(int n)
{
    int sum = 0;
    if (0 == n)
        return 1;
    else
        sum = n * factorial(n-1);
    return sum;
}

3.3 樹的遍歷

二叉樹的遍歷代碼如下:

/*
  前序遍歷算法
*/
void PreOderTraverse(BiTree T)
{
    if(T == NULL)
        return;
    printf("%c",T->data);  //顯示結點數據,可以更改為其他對結點操作
    PreOderTraverse(T->lchild);   //先遍歷左子樹
    PreOderTraverse(T->rchild);    //最后遍歷右子樹 
} 
 /*
   中序遍歷遞歸算法
 */
void InOderTraverse(BiTree T)
{
    if(T == NULL)
        return ;
    InOderTraverse(T->lchild);   //中序遍歷左子樹
    printf("%c",T->data);   //顯示結點數據,可以更改為其他對結點的操作
    InOderTraverse(T->rchild);  //最后中序遍歷右子樹 
} 
 /*
   后序遍歷遞歸算法
 */
void PostOderTraverse(T)
{
    if(T==NULL)
        return;
    PostOderTraverse(T->lchild);   //先遍歷左子樹 
    PostsOderTraverse(T->rchild);  //再遍歷右子樹 
    printf("%c",T->data);    //顯示結點數可以更改為其他對結點數據 
} 

通過代碼可以看出,二叉樹的遍歷過程使用遞歸方式實現既有助於理解,又簡化了代碼量。

4 結語

使用遞歸求解問題就好比,你手中有一把鑰匙想要打開一扇門。當你打開面前這扇門,看到屋里面還有一扇門。你走過去,發現手中的鑰匙還可以打開它,你推開門,發現里面還有一扇門,你繼續打開它。若干次之后,你打開面前的門后,發現只有一間屋子,沒有門了。然后,你開始原路返回,每走回一間屋子,你數一次,走到入口的時候,你可以回答出你到底用這你把鑰匙打開了幾扇門。
  遞歸算法的應用十分廣泛,應用遞歸算法可以使你的代碼根據“優雅”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM