學習遞迴(總結)
遞迴是設計和描述演算法的一種有力的工具,由於它在複雜演算法的描述中被經常採用,為此在進一步介紹其他演算法設計方法之前先討論它。
能採用遞迴描述的演算法通常有這樣的特徵:為求解規模為n的問題,設法將它分解成規模較小的問題,然後從這些小問題的解方便地構造出大問題的解,並且這些規模較小的問題也能採用同樣的分解和綜合方法,分解成規模更小的問題,並從這些更小問題的解構造出規模較大問題的解。特別地,當規模n=1時,能直接得解。
遞迴基本格式: 1.遞迴關係式的建立(如f(n)和f(n-1)關係的建立) 2.遞迴的出口個人總結: 1.遞迴弄懂了使用是簡單的,比如**簡短,思路清晰(只要上面兩個基本格式懂了) 2.遞迴的效率不是比較高關於遞迴樹問題以後研究(樹可得惡補).
【問題】 編寫計算斐波那契(fibonacci)數列的第n項函式fib(n)。
斐波那契數列為:0、1、1、2、3、……,即:
fib(0)=0;
fib(1)=1;
fib(n)=fib(n-1)+fib(n-2) (當n>1時)。
寫成遞迴函式有:
int fib(int n)
遞迴演算法的執行過程分遞推和回歸兩個階段。在遞推階段,把較複雜的問題(規模為n)的求解推到比原問題簡單一些的問題(規模小於n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是說,為計算fib(n),必須先計算fib(n-1)和fib(n-2),而計算fib(n-1)和fib(n-2),又必須先計算fib(n-3)和fib(n-4)。依次類推,直至計算fib(1)和fib(0),分別能立即得到結果1和0。在遞推階段,必須要有終止遞迴的情況。例如在函式fib中,當n為1和0的情況。
在回歸階段,當獲得最簡單情況的解後,逐級返回,依次得到稍複雜問題的解,例如得到fib(1)和fib(0)後,返回得到fib(2)的結果,……,在得到了fib(n-1)和fib(n-2)的結果後,返回得到fib(n)的結果。
在編寫遞迴函式時要注意,函式中的區域性變數和引數知識侷限於當前呼叫層,當遞推進入「簡單問題」層時,原來層次上的引數和區域性變數便被隱蔽起來。在一系列「簡單問題」層,它們各有自己的引數和區域性變數。
由於遞迴引起一系列的函式呼叫,並且可能會有一系列的重複計算,遞迴演算法的執行效率相對較低。當某個遞迴演算法能較方便地轉換成遞推演算法時,通常按遞推演算法編寫程式。例如上例計算斐波那契數列的第n項的函式fib(n)應採用遞推演算法,即從斐波那契數列的前兩項出發,逐次由前兩項計算出下一項,直至計算出要求的第n項。
【問題】 組合問題
問題描述:找出從自然數1、2、……、n中任取r個數的所有組合。例如n=5,r=3的所有組合為: (1)5、4、3 (2)5、4、2 (3)5、4、1
(4)5、3、2 (5)5、3、1 (6)5、2、1
(7)4、3、2 (8)4、3、1 (9)4、2、1
(10)3、2、1
分析所列的10個組合,可以採用這樣的遞迴思想來考慮求組合函式的演算法。設函式為void comb(int m,int k)為找出從自然數1、2、……、m中任取k個數的所有組合。當組合的第乙個數字選定時,其後的數字是從餘下的m-1個數中取k-1數的組合。這就將求m個數中取k個數的組合問題轉化成求m-1個數中取k-1個數的組合問題。設函式引入工作陣列a[ ]存放求出的組合的數字,約定函式將確定的k個數字組合的第乙個數字放在a[k]中,當乙個組合求出後,才將a[ ]中的乙個組合輸出。第乙個數可以是m、m-1、……、k,函式將確定組合的第乙個數字放入陣列後,有兩種可能的選擇,因還未去頂組合的其餘元素,繼續遞迴去確定;或因已確定了組合的全部元素,輸出這個組合。細節見以下程式中的函式comb。
【程式】
# include
# define maxn 100
int a[maxn];
void comb(int m,int k)
} }
void main()
【問題】 揹包問題
問題描述:有不同價值、不同重量的物品n件,求從這n件物品中選取一部分物品的選擇方案,使選中物品的總重量不超過指定的限制重量,但選中物品的價值之和最大。
設n件物品的重量分別為w0、w1、…、wn-1,物品的價值分別為v0、v1、…、vn-1。採用遞迴尋找物品的選擇方案。設前面已有了多種選擇的方案,並保留了其中總價值最大的方案於陣列option[ ],該方案的總價值存於變數maxv。當前正在考察新方案,其物品選擇情況儲存於陣列cop[ ]。假定當前方案已考慮了前i-1件物品,現在要考慮第i件物品;當前方案已包含的物品的重量之和為tw;至此,若其餘物品都選擇是可能的話,本方案能達到的總價值的期望值為tv。演算法引入tv是當一旦當前方案的總價值的期望值也小於前面方案的總價值maxv時,繼續考察當前方案變成無意義的工作,應終止當前方案,立即去考察下乙個方案。因為當方案的總價值不比maxv大時,該方案不會被再考察,這同時保證函式後找到的方案一定會比前面的方案更好。
對於第i件物品的選擇考慮有兩種可能:
(1) 考慮物品i被選擇,這種可能性僅當包含它不會超過方案總重量限制時才是可行的。選中後,繼續遞迴去考慮其餘物品的選擇。
(2) 考慮物品i不被選擇,這種可能性僅當不包含物品i也有可能會找到價值更大的方案的情況。
按以上思想寫出遞迴演算法如下:
try(物品i,當前選擇已達到的重量和,本方案可能達到的總價值tv)
/*考慮物品i不包含在當前方案中的可能性*/
if (不包含物品i僅是可男考慮的)
if (i
try(i+1,tw,tv-物品i的價值);
else
/*又乙個完整方案,因它比前面的方案好,以它作為最佳方案*/
以當前方案作為臨時最佳方案儲存;
} 為了理解上述演算法,特舉以下例項。設有4件物品,它們的重量和價值見表:
物品 0 1 2 3
重量 5 3 2 1
價值 4 4 3 1
並設限制重量為7。則按以上演算法,下圖表示找解過程。由圖知,一旦找到乙個解,演算法就進一步找更好的佳。如能判定某個查詢分支不會找到更好的解,演算法不會在該分支繼續查詢,而是立即終止該分支,並去考察下乙個分支。
按上述演算法編寫函式和程式如下:
【程式】
# include
# define n 100
double limitw,totv,maxv;
int option[n],cop[n];
struct a[n];
int n;
void find(int i,double tw,double tv)
cop=0;
} /*考慮物品i不包含在當前方案中的可能性*/
if (tv-a.value>maxv)
if (i
else
} void main()
printf(「輸入限制重量\n」);
scanf(「%1f」,&limitv);
maxv=0.0;
for (k=0;k
find(0,0.0,totv);
for (k=0;k
if (option[k]) printf(「%4d」,k+1);
printf(「\n總價值為%.2f\n」,maxv);
} 作為對比,下面以同樣的解題思想,考慮非遞迴的程式解。為了提高找解速度,程式不是簡單地逐一生成所有候選解,而是從每個物品對候選解的影響來形成值得進一步考慮的候選解,乙個候選解是通過依次考察每個物品形成的。對物品i的考察有這樣幾種情況:當該物品被包含在候選解中依舊滿足解的總重量的限制,該物品被包含在候選解中是應該繼續考慮的;反之,該物品不應該包括在當前正在形成的候選解中。同樣地,僅當物品不被包括在候選解中,還是有可能找到比目前臨時最佳解更好的候選解時,才去考慮該物品不被包括在候選解中;反之,該物品不包括在當前候選解中的方案也不應繼續考慮。對於任一值得繼續考慮的方案,程式就去進一步考慮下乙個物品。
【程式】
# include
# define n 100
double limitw;
int cop[n];
struct ele a[n];
int k,n;
struct twv[n];
void next(int i,double tw,double tv)
double find(struct ele *a,int n)
else
break;
case 0: i--;
break;
default: twv.***=0;
if (tv-a.value>maxv)
if (i
else
break;
} }
return maxv;
} void main()
JAVA Fiel 遞迴 總結
遞迴 方法定義中呼叫方法本身的現象 直接遞迴 public void methoda 間接遞迴 public void metohdb public void methodc 遞迴注意實現 要有出口,否則就是死遞迴 次數不能太多,否則就記憶體溢位 file 檔案和目錄路徑名的抽象表示形式 構造方法 ...
「Java 遞迴」總結
color red 遞迴 recursion 就是方法呼叫自身。對於遞迴來說,一定有乙個出口,讓遞迴結束,只有這樣才能保證不出現死迴圈 color 遞迴的 很容易出錯,很難除錯。下面是乙個簡單的遞迴例項 package com.shengshiyuan.recursion public class ...
函式遞迴總結
函式的遞迴 例1.接收乙個整型值,並且按順序列印他的每一位.例如 輸入 1234.輸出 1 2 3 4 include include void print int num printf d n num 10 intmain 執行結果 例2.求字串的長度 法1.利用strlen函式直接求 inclu...