递归算法浅谈
遞歸算法
程序調用自身的編程技巧稱為遞歸( recursion)。
一個過程或函數在其定義或說明中又直接或間接調用自身的一種方法,它通常把一個大型復雜的問題層層轉化為一個與原問題類似的規模較小的問題來求解,遞歸策略僅僅需少量的程序就可描寫敘述出解題過程所須要的多次反復計算,大大地降低了程序的代碼量。
注意:
(1) 遞歸就是在過程或函數里調用自身;
(2) 在使用遞增歸策略時,必須有一個明白的遞歸結束條件,稱為遞歸出口。
一個比較經典的描寫敘述是老和尚講故事,他說從前有座山,山上有座廟,廟里有個老和尚在講故事,他說從前有座山,山上有座廟,廟里有個老和尚在講故事,他說從前有座山, ……。這樣沒完沒了地重復講故事,直到最后老和尚煩了停下來為止。
重復講故事能夠看成是重復調用自身,但假設不能停下來那就沒有意義了,所以終于還要能停下來。遞歸的關鍵在于找出遞歸方程式和遞歸終止條件。即老和尚重復講故事這種遞歸方程式要有,最后老和尚煩了停下來這種遞歸的終止條件也要有。
階乘的算法能夠定義成函數
當 n>0時,用 f(n-1)來定義 f(n),用 f(n-1-1)來定義 f(n-1)……,這是對遞歸形式的描寫敘述。
當 n=0時, f(n)=1,這是遞歸結束的條件。
事實上全部的遞歸問題都能夠看成是階層問題
所要解決的整個問題(整個遞歸函數)看成是?f(n).在這個遞歸函數中要做到例如以下幾點:
? *1.寫出遞歸的出口
? *2.解決當前要解決的問題-----相當與階層問題中的(n)
? *3.遞歸下去(調用自身)解決同樣的但規模要小的又一問題-----相當于f(n-1)
假設函數實現了這幾點,那么遞歸算法也就基本成功.
只是有些問題他的f(n-1)可能要調用幾次(可能每次的參數還不同),由于他在實現(n)的時候要先調用f(n-1)為前提,
這種樣例非常多.比方梵塔問題就是這種情況.
總而言之,你要懂的把復雜的遞歸問題遷移成簡單的階層遞歸問題,這時候問題就比較好理解和解決.
以下是幾個用遞歸解決這個問題的幾個樣例
一.用遞歸的算法把數組中的N個數按顛倒的次序又一次存放。
經過上面的方法來分析得出程序例如以下:
package sf.digui;
public class Recursion{
?private int b[]=null;
?private int len=0;
?public Recursion(int b[]){
??this.b=b;
??this.len=b.length;
??}
??
??public void resevert(int i,int j){
???if(i>=j) return;
???//====================
???b[i]=b[i]+b[j];
???b[j]=b[i]-b[j];//注意:這里沒有通過第三方(另開內存)來實現兩個變量的值交換
???b[i]=b[i]-b[j];
???//=========================
???
???resevert(i+1,j-1);
???}
???
???public void printThis(){
????
????for(int i=0;i<len;i++){
?????System.out.print(b[i]+" ");
?????
?????}
?????System.out.println();
????}
????
????
????public static void main(String[] args){
?????int b[]={1,2,3,4,5,6,7,8,9};
?????int len=b.length;
?????Recursion rec=new Recursion(b);
?????System.out.println("數組起始的數為:");
?????rec.printThis();
?????rec.resevert(0,len-1);
?????System.out.println("數組經過倒轉之后的數為:");
?????rec.printThis();
?????}
?}
二..用遞歸算法完畢:有52張牌,使它們所有正面朝上,第一輪是從第2張開始,凡是2的倍數位置上的牌翻成正面朝下;第二輪從第3張牌開始,凡是3的倍數位置上的牌,正面朝上的翻成正面朝下,正面朝下的翻成正面朝上;第三輪從第4張牌開始,凡是4的倍數位置上的牌按上面同樣規則翻轉,以此類推,直到第一張要翻的牌超過52為止。統計最后有幾張牌正面朝上,以及它們的位置號。
經過上面的方法分析,得出程序例如以下:
package sf.digui;
public class DiGui{
?private int n;
?//private int a;
?private int p[]=null;//存放全部牌的正反面信息
?public DiGui(int n){
??this.n=n;
??//a=n;
??p=new int[n];
??for(int i=0;i<n;i++){
???p[i]=0;//這里0表示是正面,1表示是反面
???}
??}
??
??
??public void process(int a){//======相當于f(n)
???int b=a;
???if(a==1) return;// 遞歸的出口
??//以下部分為解決當前要做的(能夠從最后一步或第一步著手思考要做的事)---相當與(n)
??//===================================================????
????while(b<=n){//
?????p[b-1]=(p[b-1]==0)?1:0;//
?????b=2*b;//
?????}
??//====================================================??
????process(a-1);//調用自身,解決同樣的但規模要小的又一問題---相當于f(n-1)
????
????
???}
???
???public void printThis(){
????process(n);
????for(int i=0;i<n;i++){
?????System.out.println("第"+(i+1)+"張的正反面序號為:"+p[i]);
?????}
????}
????
????
????public static void main(String[] args){
?????DiGui digui=new DiGui(52);
?????digui.printThis();
?????}
?}
?
?
?/*說明:
? *我覺得全部遞歸都可看成像階層一樣的問題---相當于f(n)=n+f(n-1),都要做遞歸函數中
? *解決例如以下幾個問題:
? *1.寫出遞歸的出口
? *2.解決當前要解決的問題-----相當與階層問題中的(n)
? *3.遞歸下去(調用自身)解決同樣的但規模要小的又一問題-----相當于f(n-1)
? *
? *
? *要學會把復雜的遞歸問題遷移成像階層一樣簡單的遞歸問題
? **/
?
以上是我學習遞歸的一些想法,希望有很多其它人回復,大家一起來談論,交流,共同進步.
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: Workbox CLI v3.x 中文版
- 下一篇: 灯具类产品各国EMC认证标准大全