尾遞歸是個什麼鬼

瞭解尾遞歸以前,先了解一下尾調用。html

在計算機科學裏,尾調用是指一個函數裏的最後一個動做是一個函數調用的情形:即這個調用的返回值直接被當前函數返回的情形。這種情形下該調用位置爲尾位置。(摘自維基百科)算法

以上的解釋來自維基百科。介紹了什麼叫尾調用。例如:網絡

function foo(data) {
    a(data);
    return b(data);
}

  

這裏的a(data)和b(data)都是函數調用,可是b(data)是函數返回前的最後運行的東西,因此也是所謂的尾位置。例如:函數

function foo1(data) {
    return a(data) + 1;
}
function foo2(data) {
    var ret = a(data);
    return ret;
}
function foo3(data) {
    var ret = a(data);
    return (ret === 0) ? 1 : ret;
}

  

這種就不是尾調用,對於foo1,最後一個動做是+1操做,並不是是直接函數調用;對於foo3,是通過計算返回的結果,也不是尾調用。,foo2也不是尾調用測試

尾調用很重要的特性就是它能夠不在調用棧上面添加一個新的堆棧幀,而是更新它。ui

接下來講一下什麼是尾遞歸:spa

若一個函數在尾位置調用自己(或是一個尾調用自己的其餘函數等),則稱這種狀況爲尾遞歸,是遞歸的一種特殊情形。而形式上只要是最後一個return語句返回的是一個完整函數,它就是尾遞歸。這裏注意:尾調用不必定是遞歸調用,可是尾遞歸必定是尾調用。日誌

接下來經過斐波那契數列和階乘來進一步理解尾遞歸的含義。code

斐波那契數列htm

 常規的斐波那契數列算法多是這樣的:

int fib(int n) {

    if (n <= 2) {
        return 1;
    }
    return fib(n - 1) + fib(n - 2);
}

  

上面的這種遞歸計算最終的return操做是加法操做。因此不是尾遞歸。

若是用尾遞歸就是這樣的:

/**
 計算第n位斐波那契數列的值
 
 @param n 第n個數
 @param acc1 第n個數
 @param acc2 第n與第n+1個數的和
 @return 返回斐波那契數列值
 */
int tailfib(int n,int acc1,int acc2) {
    if (n < 2) {
        return acc1;
    }
    
    return tailfib(n-1,acc2,acc1 + acc2); 
}

  

好比咱們想計算第10位斐波那契數列的值,只須要fib(10,1,1)便可。

看一下測試效果,測試程序以下:

int main(int argc, const char * argv[]) {
    clock_t start,finish;
   
    start = clock();
    printf("計算結果:%d\n", fib(45));
    finish = clock();
    printf("花費時間--------%lu\n",finish - start);

    
    start = clock();
    printf("計算結果:%d\n", tailfib(45,1,1));
    finish = clock();
    
    printf("花費時間--------%lu\n",finish - start);
    return 0;
    
}

  

計算結果以下:

計算結果:1134903170
花費時間--------5540692
計算結果:1134903170
花費時間--------4
Program ended with exit code: 0

  

效率可想而知。

 

階乘

常規的計算階乘的方法多是這樣的:

int fac(int n) {
    if (n == 1) {
        return 1;
    }
    return fac(n-1) * n;
}

複雜度爲O(n)

尾遞歸的算法是這樣的:

int tailfac(int n,int sum) {
    if (n == 1) {
        return sum;
    }
    return fac(n-1, n * sum);
}

 複雜度爲O(1)

測試一下效率,測試程序以下:

int main(int argc, const char * argv[]) {
    clock_t start,finish;
   
    start = clock();
    printf("計算結果:%d\n", fac(16));
    finish = clock();
    printf("花費時間--------%lu\n",finish - start);

    
    start = clock();
    printf("計算結果:%d\n", tailfac(16,1));
    finish = clock();
    
    printf("花費時間--------%lu\n",finish - start);
    return 0;
    
}

  

測試結果:

計算結果:2004189184
花費時間--------31
計算結果:2004189184
花費時間--------2

  

尾遞歸效率比較高,可是我的以爲有尾遞歸算法理解起來會比較困難,你須要標註一下每一個傳入參數的做用,不然剛接觸不必定會用這個算法。

 

參考資料:阮一峯的網絡日誌 維基百科

轉載請標註來源:http://www.cnblogs.com/zhanggui/p/7722541.html

相關文章
相關標籤/搜索