詳細解釋遞歸原理

一直以來對於遞歸原理都不是很瞭解,最近找實習非得要學習了,於是今天好好的研究了一下,這裏以《劍指offer》上面第93頁遞歸的例子來進行分析。
問題是:求a的n次方。
下面是一個比較簡單的求法的公式:

an={an/2an/2n爲偶數a(n1)/2a(n1)/2an爲奇數

由以上公式就可以寫出下面的代碼:

double Power(double base, unsigned int exponent) {
    // 遞歸的終止條件
    if (exponent == 0)
        return 1;
    if (exponent == 1)
        return base;
    // 遞歸進棧和出棧處    
    double result = PowerExponent(base, exponent >> 1);
    result *= result;
    if ((exponent & 0x1)== 1) {

        result *= base;
        cout << result << endl;
    }
    return result;
}

這裏拿一個簡單的例子來進行分析:
假設我們調用函數爲:Power(3, 15);

1、Power(3, 15)中base=3,exponent=15,遞歸的終止條件均不滿足,進入到 遞歸進棧和出棧處,此時result = Power(3, 7)(此處exponent >> 1的運算過程爲:exponent=15,用二進制表示爲1111,右移一位即爲0111,表示7,所以exponent變爲7,下同)進棧;
2、Power(3, 7)中base=3,exponent=7,遞歸的終止條件均不滿足,進入到遞歸進棧和出棧處,此時result = Power(3, 3)進棧;
3、Power(3, 3)中base=3,exponent=3,遞歸的終止條件均不滿足,進入到遞歸進棧和出棧處,此時result = Power(3, 1)進棧;

此時exponent=1,遞歸的終止條件第二個if條件滿足(注意此處即使有兩個終止條件也不要繼續往下計算,因爲函數遇到return就會終止,每個函數只有一個return),因此全部進棧完畢,開始出棧

4、Power(3, 1)出棧,base=3,exponent=1,可以看做執行函數如下:

double Power(double base, unsigned int exponent) {
    if (exponent == 1)
        return base;
}

即此時在遞歸進棧和出棧處result = Power(3, 1) = base = 3

5、Power(3, 3)出棧,base=3,exponent=3,可以看做執行函數如下:

double Power(double base, unsigned int exponent) {
    // 即爲將Power(3, 1)代入函數中來計算Power(3, 3)的值
    double result = Power(3, 1);
    result *= result;
    if ((exponent & 0x1)== 1) {

        result *= base;
        cout << result << endl;
    }
    return result;
}

即此時在遞歸進棧和出棧處result = Power(3, 3) = Power(3, 1)* Power(3, 1) * base = 3*3*3 = 27(最後乘base因爲exponent=3滿足if的條件,其中if的條件中(exponent & 0x1)指的是將exponent與八進制的1即用二進制表示爲0001作位與運算,即判斷exponent的二進制形式最後一位是否爲1,即判斷exponent是否爲奇數,下同)

6、Power(3, 7)出棧,base=3,exponent=7,可以看做執行函數如下:

double Power(double base, unsigned int exponent) {
    // 即爲將Power(3, 3)代入函數中來計算Power(3, 7)的值
    double result = Power(3, 5);
    result *= result;
    if ((exponent & 0x1)== 1) {

        result *= base;
        cout << result << endl;
    }
    return result;
}

即此時在遞歸進棧和出棧處result = Power(3, 7) = Power(3, 3)* Power(3, 3) * base = 27*27*3 = 2187(最後乘base因爲exponent=3滿足if的條件)

7、最後計算Power(3, 15),base=3,exponent=15,可以看做執行函數如下:

double Power(double base, unsigned int exponent) {
    // 即爲將Power(3, 7)代入函數中來計算Power(3, 15)的值
    double result = Power(3, 7);
    result *= result;
    if ((exponent & 0x1)== 1) {

        result *= base;
        cout << result << endl;
    }
    return result;
}

即此時result = Power(3, 15) = Power(3, 7)* Power(3, 7) * base = 2187*2187*3 = 14348907(最後乘base因爲exponent=3滿足if的條件)

(注意出棧的4,5,6,7步中實際的運行是會重新運行一遍整個函數的,此處爲了強調遞歸的終止和遞歸出棧後的執行部分所以將沒有運行的代碼段部分省略掉了)

規律總結

從上面的步驟分析可以看出,遞歸在沒有滿足遞歸終止條件的時候,將每次遞歸的中間值都進棧,因此每一次遞歸的過程中相當於在遞歸進棧和出棧處打了一個斷點,然後在第一次滿足遞歸終止條件時將會開始出棧,出棧實際上是從每一次停止的斷點處開始執行,將這一次遞歸得到的值返回給上一次遞歸,並以此繼續進行下去,下面幾個經典的遞歸例子可以仿照上面的分析過程進行分析。

// 計算階乘
long factorial_recursion(int n){

    if(n <= 0)
        return 1;

    else
        return n * factorial_recursion(n-1);
}
// 計算Fibonacci數列
long long Fibonacci(unsigned int n){

    if (n <= 0) 
        return 0;
    if (n == 1) 
        return 1;

    return Fibonacci(n-1) + Fibonacci(n - 2);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章