http://blog.csdn.net/effective_coder/article/details/8742979
遞歸算法詳解
C語言通過運行時堆棧來支持遞歸的調用,在我們剛接觸遞歸的時候,國內很多教材都采用求階乘和菲波那契數列來描述該思想,就如同深受大家敬愛的國產的C語言程序設計,老譚也用了階乘來描述遞歸,以至於很多新手一看見階乘就理所當然的認為是遞歸,坑了不少人,說實在的,描述這個思想還是可以,但是利用遞歸求階乘可是沒有一點好處,遞歸解決菲波那契數列效率更是低得驚人,這點是顯而易見的!廢話不多說,接下來我們進入正題!(不過說實話,我很討厭接下來這些太理論的東西,說到底就是那么個意思,大家懂就好了,也可以當看看故事!我主要說的就是各種各樣遞歸的實例)
1:遞歸算法的思想
遞歸算法是把問題轉化為規模縮小了的同類問題的子問題。然后遞歸調用函數(或過程)來表示問題的解。在C語言中的運行堆棧為他的存在提供了很好的支持,過程一般是通過函數或子過程來實現。
遞歸算法:在函數或子過程的內部,直接或者間接地調用自己的算法。
2:遞歸算法的特點:
3:遞歸算法的要求
4:各式各樣利用遞歸的問題
1:首先看看那些傳統的問題吧,如使用遞歸來解決斐波那契數列的第n個數是多少?(開始從1開始)
- #include <iostream>
- using namespace std;
- int Fib(int index);
- int main(int argc, char* argv[])
- {
- cout<<Fib(12)<<endl;
- system("pause");
- return 0;
- }
- int Fib(int index)
- {
- if(index==1 || index==2)
- return index;
- else
- return Fib(index-1) + Fib(index-2); //開始遞歸調用
- }
寫程序的時候我測試了一下,假如要第100個數字,那時間可不知道等了多久,調用函數達到了上千次,速度太慢,對於這種情況,我們對比一下不使用的遞歸的時候時間消耗,這里只需要多加一個函數即可
- #include <iostream>
- #include <ctime>
- using namespace std;
- int Fib2(int index);
- int Fib1(int index);
- int main(int argc, char* argv[])
- {
- clock_t start,finish;
- cout<<"不使用遞歸:"<<endl;
- start = clock();
- cout<<"所得結果為 "<<Fib2(40)<<endl;
- finish = clock();
- cout<<"時間消耗為 "<<finish - start<<"毫秒"<<endl;
- cout<<endl;
- cout<<"使用遞歸:"<<endl;
- start = clock();
- cout<<"所得結果為 "<<Fib1(40)<<endl;
- finish = clock();
- cout<<"時間消耗為 "<<finish - start<<"毫秒"<<endl;
- system("pause");
- return 0;
- }
- int Fib1(int index)
- {
- if(index==1 || index==2)
- return index;
- else
- return Fib1(index-1) + Fib1(index-2); //開始遞歸調用
- }
- int Fib2(int index)
- {
- if(index == 1 || index ==2)
- return index;
- int *array = new int [index+1];
- array[1]=1; //第0個元素沒有使用
- array[2]=2;
- for(int i=3;i<=index;++i)
- array[i] = array[i-1] + array[i-2];
- return array[index];
- }
運行結果:
結果顯而易見,差距太明顯,在這里我們同時求第40個斐波那契數字比較時間消耗,所以大家可以看到遞歸的時間消耗是非常嚴重,而且效率非常低下,上面已經說了,在可以不用遞歸的時候盡量不用,那么遞歸是不是一無是處勒?答案是否定的,在很多程序設計大賽中,有很多題用一般的思路是很難解的,或者是過程繁瑣,如果適當的利用遞歸,結果將事半功倍!!!
2:遞歸的漢諾塔
這個程序以及說明在分治算法那一節已經說了,遞歸和分治通常都是結合在一起使用的,一次次的縮小范圍,而且子問題和原問題具有相同的結構! 這里我直接把漢諾塔代碼拷貝過來,就不多說了!
- #include <stdio.h>
- #include <stdlib.h>
- static int count = -1;
- void move(char x,char y); // 對move函數的聲明
- void hanoi(int n,char one,char two,char three) ;// 對hanoi函數的聲明\
- int main()
- {
- int m;
- printf("請輸入一共有多少個板子需要移動:");
- scanf("%d",&m);
- printf("以下是%d個板子的移動方案:\n",m);
- hanoi(m,'A','B','C');
- system("pause");
- return 0;
- }
- void hanoi(int n,char one,char two,char three) // 定義hanoi函數
- // 將n個盤從one座借助two座,移到three座
- {
- if(n==1)
- move(one,three);
- else
- {
- hanoi(n-1,one,three,two); //首先把n-1個從one移動到two
- move(one,three); //然后把最后一個n從one移動到three
- hanoi(n-1,two,one,three); //最后再把n-1個從two移動到three
- }
- }
- void move(char x,char y) // 定義move函數
- {
- count++;
- if( !(count%5) )
- printf("\n");
- printf("%c移動至%c ",x,y);
- }
3:兔子繁殖問題(遞歸實現)
一對小兔子一年后長成大兔子,一對大兔子每半年生一對小兔子,大兔子的繁殖期為4年,兔子的壽命為6年,假定第一年年初投放了一對小兔子,請編程實現,第N年年末總共有多少只兔子,N由鍵盤輸入!
解析,這個題目比較好懂,也就是一對小兔子前一年長大,然后每半年產一對小兔子,持續4年,然后最后一年不生殖了,再過一年死亡,題目看似簡單,其實要想遞歸起來可不是那么容易的,大家可以想一下!
代碼如下:
4:整數的划分問題
將一個整數分解為若干個整數之和的形式,比如 n = n1+n2+n3+n4··········!不同划分的個數稱為N的划分數。
例如對於6而言:
6;
5+1;
4+2,4+1+1;
3+3;3+2+1;3+1+1+1;
2+2+2;2+2+1+1;2+1+1+1+1;
1+1+1+1+1+1 一共有6種!
1、 q(n,1) = 1 ,n>=1 ;
當最大加數不大於1時,任何正整數n只有一種表示方式:n = 1+1+……+1 。n個1的和。
2、q( n,m ) = q( n,n ),n<=m; 最大加數不能大於n。
3、 q( n,n ) = 1 + q( n , n-1 ); 正整數的划分由n1=n和n1<=n的划分組成。
4、q( n,m ) = q( n,m-1 )+q( n-m,m ), n>m>1;正整數n的最大加數不大於m的划分由 n1=m的划分和n1<m的划分組成。
現在可以依據這個遞推原理寫出程序:
- #include <stdio.h>
- #include <stdlib.h>
- int intPart( int n , int m ) ;
- int main()
- {
- int num ;
- int partNum = 0 ;
- printf("Please input an integer:/n") ;
- scanf("%d",&num) ;
- partNum = intPart(num,num);
- printf("%d/n",partNum) ;
- system("pause");
- return 0;
- }
- int intPart( int n , int m )
- {
- if( ( n < 1 ) ||( m < 1 ) ) return 0 ;
- if( ( n == 1 )||( m == 1 ) ) return 1 ;
- if( n < m ) return intPart( n , n ) ;
- if( n == m ) return intPart( n , m-1 ) + 1 ;
- return intPart( n , m-1 ) + intPart( n - m , m ) ;
- }
運行結果可以看到一共有11種情況
5 整數的全排列問題:
全排列的遞歸實現也就是不停的交換兩個數的位置,題目描述這里就省了,直接上代碼!
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- void swap(char *a,char *b)
- {
- char temp = *a;
- *a = *b;
- *b = temp;
- }
- //k表示循環到第幾個字符,m表示該次循環的總長度
- void arrange(char *pizstr,int k,int m)
- {
- if(k == m)
- {
- static int m_count = 1;
- printf("the %d time:%s\n",m_count++,pizstr);
- }
- else
- {
- for(int i=k;i<=m;i++) //主要遞歸球全排列的代碼
- {
- swap(pizstr+k,pizstr+i);
- arrange(pizstr,k+1,m);
- swap(pizstr+k,pizstr+i);
- }
- }
- }
- void foo(char *p_str)
- {
- arrange(p_str,0,strlen(p_str)-1);
- }
- int main()
- {
- char pstr[] = "12345";
- printf("%s\n",pstr);
- foo(pstr);
- system("pause");
- return 0;
- }
時間緊促,有時間再繼續舉例!持續更新