標籤:http 使用 io for art 問題 代碼 div
遞迴演算法
程式調用自身的編程技巧稱為遞迴( 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)
*
*
*要學會把複雜的遞迴問題遷移成像階層一樣簡單的遞迴問題
**/
以上是我學習遞迴的一些想法,希望有很多其它人回複,大家一起來談論,交流,共同進步.