遞歸定義
遞歸調用:在調用一個函數的過程中,這個函數本身又直接或間接地調用了函數本身。
通俗地說,舉個例子。有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)
總的來說,遞歸要有以下兩個條件:
- 基線條件:也就是停止條件
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桿:
- 每次只能移動一個圓盤;
- 大盤不能疊在小盤上面;
實現如下
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的排列,可以分成
-
以a開頭,加上bc的排列
-
以b開頭,加上ac的排列
-
以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;
}