遞歸用法總結


遞歸定義

遞歸調用:在調用一個函數的過程中,這個函數本身又直接或間接地調用了函數本身。
通俗地說,舉個例子。有5個學生在一起,問第5個學生有多少錢,他說比第4個學生多2塊,第四個又說比第三個多2塊,第三個比第二個多2塊,第二個又比第一個多2塊。第一個人說自己只有10塊。在這個例子中,我們求第5個人有多少錢的時候,可以通過第四個人,第四個通過第三個,一次類推。這就是遞歸的思想。
上述求學生錢的例子表達式可以用以下表達式:

    money(5) = money(4) + 2;
    money(4) = money(3) + 2;
    money(3) -= money(2) + 2;
    money(2) = money(1) +2;
    money(1) = 10;

數學公式如下

money(n) = 10;    (n=1)
money(n) = money(n-1) + 2; (n>1)

總的來說,遞歸要有以下兩個條件:

  1. 基線條件:也就是停止條件
    2.遞歸條件:遞歸關系的表述

遞歸的幾個經典題目

斐波那契數列

斐波那契數列是:0,1,1,2,3,5,8,13,21,34,55,89,144……依次類推下去,你會發現,它后一個數等於前面兩個數的和。在這個數列中的數字,就被稱為斐波那契數。
仔細觀察我們可以知道,從第三項開始,當前項都等於前兩項之和。這就是遞歸的條件。

long fac(long n){
   if(n == 1 || n == 0){
               return n;
    }
   if(n < 0)
        return -1;            //-1代表錯誤
   
   return fac(n - 1) + fac(n - 2);
}

階乘問題

遞歸表達式:n! = n * (n-1)!

long f(int n){
    if( n == 1 || n == 0)
        return 1;
    if(n < 0)
        return -1;

    return f(n-1) * n;
}

漢諾塔問題

法國數學家愛德華·盧卡斯曾編寫過一個印度的古老傳說:在世界中心貝拿勒斯(在印度北部)的聖廟里,一塊黃銅板上插着三根寶石針。印度教的主神梵天在創造世界的時候,在其中一根針上從下到上地穿好了由大到小的64片金片,這就是所謂的漢諾塔。不論白天黑夜,總有一個僧侶在按照下面的法則移動這些金片:一次只移動一片,不管在哪根針上,小片必須在大片上面。僧侶們預言,當所有的金片都從梵天穿好的那根針上移到另外一根針上時,世界就將在一聲霹靂中消滅,而梵塔、廟宇和眾生也都將同歸於盡。

簡單概述如下:
有三根桿子X,Y,Z。X桿上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至Y桿:

  1. 每次只能移動一個圓盤;
  2. 大盤不能疊在小盤上面;

實現如下

void hanoi(int n,char from,char tmp,char to){
if (n>0) {
    hanoi(n - 1, from, to, tmp);
    cout<<"take " + n + " from " + from + " to " + to;
    hanoi(n - 1, tmp, from, to);
}
}

倒敘輸出一個正整數

說明:給出正整數 n=12345,希望以各位數的逆序形式輸出,即輸出54321

遞歸思想:先輸出個位數字,然后在輸出個位數字前面的數字,知道沒有數字

代碼實現如下:

void printNum(int n){
     cout << n % 10 << " ";
     if( n > 10){
            printNum(n / 10);
     }
}

排序問題

用遞歸實現:輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則輸出由字符a、b、c所能排列出來的所有字符串abc、acb、bac、bca、cab和cba。

遞歸思想
假如針對abc的排列,可以分成

  1. 以a開頭,加上bc的排列

  2. 以b開頭,加上ac的排列

  3. 以c開頭,加上ab的排列

    void strfac(string s, int k){

     //遞歸出口條件:當交換到最后時就輸出整個字符串
     if(k == s.length())
     {
         for(int i = 0; i < k; i++){
     	    cout << s[i] << " ";
         }
         cout <<endl;
     }
    
    
     //開始遞歸的算法
     for(int i = k; i < s.length(); i++){
    
         //將k個字符交換
         char t = s[k];
         s[k] = s[i];
         s[i] = t;
    
         //而后遞歸調用k+1個字符
         strfac(s,k+1);
    
    
         //回溯后變回原來的字符
         {
     	    char t = s[k];
     	    s[k] = s[i];
     	    s[i]= t;
         }
     }
    

    }

遞歸求從 m 個球中摸n個球的排列數

int getSum(int m,int n){

//判斷n 是否大於m
if(m < n){
	cout << "摸得球數不能大於球總數" << endl;
	return 0;
}

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

return getSum(m-1,n-1) +getSum(m-1,n);

}

求兩個子串的最大公共序列長度

int getsublen(string s1,string s2){

    if(s1.length() == 0 || s2.length() == 0)
	    return 0;

    if(s1.at(0) == s2.at(0))
	    return getsublen(s1.substr(1),s2.substr(1)) + 1;
    //不相等則子串加一
    else
	    return max(getsublen(s1.substr(1),s2),getsublen(s1,s2.substr(1)));
}

int max(int a,int b){

    if(a > b)
	return a;
    else
	    return b;
}


免責聲明!

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



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