遞歸算法也是C語(yǔ)言算法中一個(gè)比較簡(jiǎn)單與常用的算法,本文我們就來(lái)談?wù)勥f歸算法,我們首先了解一下什么是遞歸算法,關(guān)于遞歸算法的概念只有一句話:一個(gè)過(guò)程(或函數(shù))直接或間接調(diào)用自己本身,這種過(guò)程(或函數(shù))叫遞歸過(guò)程(或函數(shù))
我們?cè)賮?lái)看看遞歸算法的特點(diǎn):
(1) 遞歸就是在過(guò)程或函數(shù)里調(diào)用自身。
(2) 在使用遞歸策略時(shí),必須有一個(gè)明確的遞歸結(jié)束條件,稱為遞歸出口。
(3) 遞歸算法解題通常顯得很簡(jiǎn)潔,但遞歸算法解題的運(yùn)行效率較低。所以一般不提倡用遞歸算法設(shè)計(jì)程序。
(4) 在遞歸調(diào)用的過(guò)程當(dāng)中系統(tǒng)為每一層的返回點(diǎn)、局部量等開(kāi)辟了棧來(lái)存儲(chǔ)。遞歸次數(shù)過(guò)多容易造成棧溢出等。所以一般不提倡用遞歸算法設(shè)計(jì)程序。
再看看遞歸的分類:
直接遞歸
程序設(shè)計(jì)中,過(guò)程或函數(shù)直接或者間接調(diào)用自己,就被稱為遞歸調(diào)用。子程序直接調(diào)用自己,這稱為直接遞歸;嵌套關(guān)系的子程序A和B,內(nèi)層的B調(diào)用外層的A,這是間接低歸;平級(jí)關(guān)系的子程序A和B,其中A調(diào)用了B,B調(diào)用了A,這也是間接遞歸,不過(guò),這種間接遞歸用到了"超前引用"的規(guī)則。
下面,博主找到了一些有關(guān)于遞歸算法的題目。我們看看第一個(gè)題目。
這是一道很經(jīng)典的題目:階乘。相信大部分的人都知道什么是階乘,但是你如果不知道的話,不妨參見(jiàn)一下百度百科:階乘。
好了,其實(shí)這題非常簡(jiǎn)單,我們只需要一個(gè)函數(shù)即可解決問(wèn)題。
1 int recursive(int i)
2 {
3 int sum = 0;
4 if (0 == i)
5 return (1);
6 else
7 sum = i * recursive(i-1);
8 return sum;
9 }
就是上面的遞歸函數(shù),這題我就不浪費(fèi)篇幅講解了,重點(diǎn)放在第二個(gè)例題:漢諾塔問(wèn)題。
一個(gè)廟里有三個(gè)柱子,第一個(gè)有64個(gè)盤子,從上往下盤子越來(lái)越大。要求廟里的老和尚把這64個(gè)盤子全部移動(dòng)到第三個(gè)柱子上。移動(dòng)的時(shí)候始終只能小盤子壓著大盤子。而且每次只能移動(dòng)一個(gè)。1、此時(shí)老和尚(后面我們叫他第一個(gè)和尚)覺(jué)得很難,所以他想:要是有一個(gè)人能把前63個(gè)盤子先移動(dòng)到第二個(gè)柱子上,我再把最后一個(gè)盤子直接移動(dòng)到第三個(gè)柱子,再讓那個(gè)人把剛才的前63個(gè)盤子從第二個(gè)柱子上移動(dòng)到第三個(gè)柱子上,我的任務(wù)就完成了,簡(jiǎn)單。所以他找了比他年輕的和尚(后面我們叫他第二個(gè)和尚),命令:
① 你把前63個(gè)盤子移動(dòng)到第二柱子上托福答案
② 然后我自己把第64個(gè)盤子移動(dòng)到第三個(gè)柱子上后
③ 你把前63個(gè)盤子移動(dòng)到第三柱子上
2、第二個(gè)和尚接了任務(wù),也覺(jué)得很難,所以他也和第一個(gè)和尚一樣想:要是有一個(gè)人能把前62個(gè)盤子先移動(dòng)到第三個(gè)柱子上,我再把最后一個(gè)盤子直接移動(dòng)到第二個(gè)柱子,再讓那個(gè)人把剛才的前62個(gè)盤子從第三個(gè)柱子上移動(dòng)到第三個(gè)柱子上,我的任務(wù)就完成了,簡(jiǎn)單。所以他也找了比他年輕的和尚(后面我們叫他第三和尚),命令:
① 你把前62個(gè)盤子移動(dòng)到第三柱子上
② 然后我自己把第63個(gè)盤子移動(dòng)到第二個(gè)柱子上后
③ 你把前62個(gè)盤子移動(dòng)到第二柱子上
3、第三個(gè)和尚接了任務(wù),又把移動(dòng)前61個(gè)盤子的任務(wù)依葫蘆話瓢的交給了第四個(gè)和尚,等等遞推下去,直到把任務(wù)交給了第64個(gè)和尚為止(估計(jì)第64個(gè)和尚很郁悶,沒(méi)機(jī)會(huì)也命令下別人,因?yàn)榈剿@里盤子已經(jīng)只有一個(gè)了)。
4、到此任務(wù)下交完成,到各司其職完成的時(shí)候了。完成回推了:
第64個(gè)和尚移動(dòng)第1個(gè)盤子,把它移開(kāi),然后第63個(gè)和尚移動(dòng)他給自己分配的第2個(gè)盤子。
第64個(gè)和尚再把第1個(gè)盤子移動(dòng)到第2個(gè)盤子上。到這里第64個(gè)和尚的任務(wù)完成,第63個(gè)和尚完成了第62個(gè)和尚交給他的任務(wù)的第一步。
從上面可以看出,只有第64個(gè)和尚的任務(wù)完成了,第63個(gè)和尚的任務(wù)才能完成,只有第2個(gè)和尚----第64個(gè)和尚的任務(wù)完成后,第1個(gè)和尚的任務(wù)才能完成。這是一個(gè)典型的遞歸問(wèn)題。 現(xiàn)在我們以有3個(gè)盤子來(lái)分析:
第1個(gè)和尚命令:
① 第2個(gè)和尚你先把第一柱子前2個(gè)盤子移動(dòng)到第二柱子。(借助第三個(gè)柱子)
② 第1個(gè)和尚我自己把第一柱子最后的盤子移動(dòng)到第三柱子。
③ 第2個(gè)和尚你把前2個(gè)盤子從第二柱子移動(dòng)到第三柱子。
很顯然,第二步很容易實(shí)現(xiàn)托福答案
其中第一步,第2個(gè)和尚他有2個(gè)盤子,他就命令:
① 第3個(gè)和尚你把第一柱子第1個(gè)盤子移動(dòng)到第三柱子。(借助第二柱子)
② 第2個(gè)和尚我自己把第一柱子第2個(gè)盤子移動(dòng)到第二柱子上。
③ 第3個(gè)和尚你把第1個(gè)盤子從第三柱子移動(dòng)到第二柱子。
同樣,第二步很容易實(shí)現(xiàn),但第3個(gè)和尚他只需要移動(dòng)1個(gè)盤子,所以他也不用在下派任務(wù)了。(注意:這就是停止遞歸的條件,也叫邊界值)
第三步可以分解為,第2個(gè)和尚還是有2個(gè)盤子,命令:
① 第3個(gè)和尚你把第二柱子上的第1個(gè)盤子移動(dòng)到第一柱子。
② 第2個(gè)和尚我把第2個(gè)盤子從第二柱子移動(dòng)到第三柱子。
③ 第3個(gè)和尚你把第一柱子上的盤子移動(dòng)到第三柱子。
分析組合起來(lái)就是:1→3 1→2 3→2 借助第三個(gè)柱子移動(dòng)到第二個(gè)柱子 |1→3 | 2→1 2→3 1→3借助第一個(gè)柱子移動(dòng)到第三個(gè)柱子|共需要七步。
如果是4個(gè)盤子,則第一個(gè)和尚的命令中第1步和第3步各有3個(gè)盤子,所以各需要7步,共14步,再加上第1個(gè)和尚的1步,所以4個(gè)盤子總共需要移動(dòng)7+1+7=15步,同樣,5個(gè)盤子需要15+1+15=31步,6個(gè)盤子需要31+1+31=64步……由此可以知道,移動(dòng)n個(gè)盤子需要(2的n次方)-1步。
從上面整體綜合分析可知把n個(gè)盤子從1座(相當(dāng)?shù)谝恢樱┮频?座(相當(dāng)?shù)谌樱?br /> (1)把1座上(n-1)個(gè)盤子借助3座移到2座。
(2)把1座上第n個(gè)盤子移動(dòng)3座。
(3)把2座上(n-1)個(gè)盤子借助1座移動(dòng)3座。
下面用hanoi(n,a,b,c)表示把1座n個(gè)盤子借助2座移動(dòng)到3座。
很明顯: (1)步上是 hanoi(n-1,1,3,2) (3)步上是 hanoi(n-1,2,1,3)
用C語(yǔ)言表示出來(lái),就是:
1 #include <stdio.h>
2 int method(int n,char a, char b)
3 {
4 printf("number%dform%cto%c"n",n,a,b);
5 return 0;
6 }
7 int hanoi(int n,char a,char b,char c)
8 {
9 if( n==1 ) move (1,a,c);
10 else
11 {
12 hanoi(n-1,a,c,b);
13 move(n,a,c);
14 hanoi(n-1,b,a,c);
15 };
16 return 0;
17 }
18 int main()
19 {
20 int num;
21 scanf("%d",&num);
22 hanoi(num,'A','B','C');
23 return 0;
24 }
這就是遞歸算法,其實(shí),在C語(yǔ)言中,遞歸算法比枚舉法還要實(shí)用,但是這兩種算法都很簡(jiǎn)單。