【算法基础】经典例题说递归
目錄
- 【算法基礎(chǔ)】經(jīng)典例題說(shuō)遞歸
- 遞歸簡(jiǎn)介
- 遞歸的適用范圍
- 遞歸的基本思路
- 經(jīng)典例題解析
- 移梵塔
- 題目描述
- 題目分析
- 題解
- 九連環(huán)
- 題目描述
- 題目分析
- 題解
- 更新日志
【算法基礎(chǔ)】經(jīng)典例題說(shuō)遞歸
遞歸簡(jiǎn)介
遞歸是一種編程技巧,一種解決問(wèn)題的思維方式。通過(guò)函數(shù)的自我調(diào)用寫(xiě)出簡(jiǎn)潔漂亮、可解釋性強(qiáng)的代碼。
遞歸:參見(jiàn)遞歸。
遞歸的適用范圍
遞歸的思維方式適用于解決層層嵌套、層層相似的問(wèn)題。要解決本層問(wèn)題就需要先把上一層問(wèn)題解決掉,而且每一層的解決方法與上一層的解決方法相似。(就像套娃一樣,一層套一層,每層都長(zhǎng)得差不多)
生活中許多問(wèn)題都滿足層層嵌套的特性。如漢諾塔、九連環(huán)的解法,謝爾賓斯基三角形的繪制,二進(jìn)制數(shù)的進(jìn)位…
拓展閱讀:
漢諾塔,二進(jìn)制和謝爾賓斯基三角形(上)
漢諾塔,二進(jìn)制和謝爾賓斯基三角形(下)
遞歸的基本思路
遞歸最重要的就是找到實(shí)現(xiàn)需求的過(guò)程中反反復(fù)復(fù)一直在執(zhí)行的核心操作是什么,并且找到和核心操作不太一樣的邊界操作,從而確定觸底條件。
(這個(gè)在觸底條件上,和核心操作不太一樣的操作,就像套娃里面最小的那個(gè)套娃,和別的大套娃不一樣,它肚子不開(kāi)口。邊界操作不再自我調(diào)用。)
層層深入->觸底->層層返回
層層深入(核心操作的自我調(diào)用,增加遞歸深度)
觸底(不再自我調(diào)用,開(kāi)始返回的分界點(diǎn))
層層返回(return)
經(jīng)典例題解析
移梵塔
時(shí)間限制: 1 Sec 內(nèi)存限制: 64 MB
題目描述
有三根柱A、B、C,在A柱上有n塊盤(pán)片,所有盤(pán)片都是大片在下面,小片放在大片上面。并依次編好序號(hào)。現(xiàn)要將A上的n塊盤(pán)片移到C柱上,每次只能移動(dòng)一片,而且在同一根柱子上必須保持上面的盤(pán)片比下面的盤(pán)片小,請(qǐng)輸出移動(dòng)方法。
輸入
僅一個(gè)整數(shù)n(n≤20),表示A柱上的盤(pán)片數(shù)。
輸出
輸出盤(pán)片的移動(dòng)步驟。
樣例輸入
3
樣例輸出
A-1-C
A-2-B
C-1-B
A-3-C
B-1-A
B-2-C
A-1-C
題目分析
讀題可知漢諾塔的解法符合層層嵌套、層層相似的特性,故采用遞歸算法。
任意盤(pán)片堆都是由一個(gè)更小的小盤(pán)片堆加一片大盤(pán)片構(gòu)成的。想要讓這個(gè)盤(pán)片堆移動(dòng),就必須三步走:①先把小盤(pán)片堆移動(dòng)到暫存柱上,②這樣大盤(pán)片才能移動(dòng)到目標(biāo)柱,③最后小盤(pán)片堆移動(dòng)到大盤(pán)片上。
因?yàn)樵谕桓由媳仨毐3稚厦娴谋P(pán)片比下面的盤(pán)片小,所以移動(dòng)小盤(pán)片堆使小盤(pán)片堆離開(kāi)初始柱的操作可以等價(jià)于移動(dòng)小盤(pán)片堆中最大的盤(pán)片。
在這里比較特殊的是第一片盤(pán)片,它上面沒(méi)有其他盤(pán)片,可以自由移動(dòng)。
核心操作:把小盤(pán)片堆從A柱移動(dòng)到C柱
邊界操作:移動(dòng)第一片盤(pán)片
觸底條件:正在移動(dòng)的盤(pán)片是第一片盤(pán)片
題解
先放完整代碼(C語(yǔ)言)對(duì)整體算法有個(gè)宏觀理解:
#include <stdio.h>void hanoi(int num,char a,char b,char c){if(num==1){printf("%c-%d-%c\n",a,num,c);}else{hanoi(num-1,a,c,b);printf("%c-%d-%c\n",a,num,c);hanoi(num-1,b,a,c);} }int main() {int num;scanf("%d",&num);hanoi(num,'A','B','C');return 0; }接著是注釋版本,可以對(duì)照理解:
#include <stdio.h>//hanoi():第num片盤(pán)片從起始柱a移動(dòng)到目標(biāo)柱c //b為盤(pán)片移動(dòng)三步走中會(huì)用到的暫存柱 void hanoi(int num,char a,char b,char c){if(num==1){//當(dāng)前移動(dòng)盤(pán)片為第一片盤(pán)片的時(shí)候直接移動(dòng)就好啦printf("%c-%d-%c\n",a,num,c);/*↑【注意】↑只有一步所以直接打印了*/}else{//盤(pán)片移動(dòng)三步走hanoi(num-1,a,c,b);//小盤(pán)片堆從起始柱移動(dòng)到暫存柱(a->b)printf("%c-%d-%c\n",a,num,c);//大盤(pán)片從起始柱移動(dòng)到目標(biāo)柱(a->c)/*↑【注意】↑只有一步所以直接打印了*/hanoi(num-1,b,a,c);//小盤(pán)片堆從暫存柱移動(dòng)到目標(biāo)柱(b->c)} }int main() {int num;scanf("%d",&num);//讀入盤(pán)片總數(shù)numhanoi(num,'A','B','C');return 0; }九連環(huán)
時(shí)間限制: 1 Sec 內(nèi)存限制: 64 MB
題目描述
九連環(huán)是由九個(gè)彼此套接的圓環(huán)和一根橫桿組成,九個(gè)環(huán)從左到右依次為l~9,每個(gè)環(huán)有兩種狀 態(tài):1和0,1表示環(huán)在桿上,0表示環(huán)不在桿上。初始狀態(tài)是九個(gè)環(huán)都在桿上,即:111111111,目標(biāo)狀態(tài)是九個(gè)環(huán)都不在桿上,即:000000000,由初始狀態(tài)到目標(biāo)狀態(tài)的變化規(guī)則是:
(1)第一環(huán)為無(wú)論何時(shí)均可自由上下橫行;
(2)第二只環(huán)只有在第一環(huán)為1時(shí),才能自由上下;
(3)想要改變第n(n>2)個(gè)環(huán)的狀態(tài),需要先使第一到第(n-2)環(huán)均為下桿,且第n-1個(gè)環(huán)為上桿,而與第n+l個(gè)到第九環(huán)狀態(tài)無(wú)關(guān);
(4)每改變一個(gè)環(huán),記為一步。
現(xiàn)在九連環(huán)由111111111變到000000000,求中間第i步的狀態(tài)。
輸入
僅包含一個(gè)整數(shù)i。
輸出
僅包含中間第i步的狀態(tài)。如果輸入的步數(shù)大于實(shí)際變換所需的步數(shù),則輸出-1。
樣例輸入
【樣例1】
2
【樣例2】
500
樣例輸出
【樣例1】
010111111
【樣例2】
-1
題目分析
讀題可知九連環(huán)的解法符合層層嵌套、層層相似的特性,故采用遞歸算法。
核心操作:解環(huán)
想要改變第n(n>2)個(gè)環(huán)的狀態(tài),需要先使第一到第(n-2)環(huán)均為下桿,且第n-1個(gè)環(huán)為上桿,而與第n+l個(gè)到第九環(huán)狀態(tài)無(wú)關(guān);
邊界操作:解第一個(gè)環(huán)
(1)第一環(huán)為無(wú)論何時(shí)均可自由上下橫行;
觸底條件:當(dāng)前在解的環(huán)是第一個(gè)環(huán)
分析解環(huán)過(guò)程,解環(huán)會(huì)對(duì)前面的環(huán)的狀態(tài)產(chǎn)生影響,但是不會(huì)對(duì)后面的環(huán)產(chǎn)生影響。所以從后往前解環(huán)才能把九連環(huán)解開(kāi)。
題解
先放完整代碼(C語(yǔ)言)對(duì)整體算法有個(gè)宏觀理解:
#include <stdio.h>int step; int pos[10]={1,1,1,1,1,1,1,1,1,1};void turn(int num){if(step==0) return;if(pos[num-1]==0) turn(num-1);for(int i=num-2;i>=1;i--){if(pos[i]==1) turn(i);}if(step==0) return;pos[num]=(pos[num]+1)%2;step--; }int main() {scanf("%d",&step);for(int i=9;i>=1;i--){turn(i);}if(step==0){for(int i=1;i<=9;i++){printf("%d",pos[i]);}putchar('\n');}else if(step>0){puts("-1");}return 0; }然后這里是非常非常詳細(xì)的注釋,看不懂可以對(duì)照著看:
#include <stdio.h>//step:操作步數(shù),在turn()函數(shù)中倒數(shù)計(jì)數(shù) int step;//pos數(shù)組:記錄環(huán)的情況 /*【注意】只有九個(gè)環(huán)但是我開(kāi)了10個(gè)位置 的數(shù)組,1~9才是記錄環(huán)的情況的,其中pos[0] 記錄的1是為了減少代碼量,避免第一個(gè)環(huán)的情況 需要單獨(dú)列出。*/ int pos[10]={1,1,1,1,1,1,1,1,1,1};//turn()函數(shù):改變第num個(gè)環(huán)的狀態(tài),套上或卸下 /*這個(gè)函數(shù)采用了遞歸的方法,但是它的觸底條件比較隱蔽, 仔細(xì)觀察可以發(fā)現(xiàn)當(dāng)num=1時(shí)函數(shù)就不再自我調(diào)用了, 它就是邊界操作。所以,num=1是觸底條件。*/ void turn(int num){/*當(dāng)步數(shù)走完就需要讓函數(shù)返回,不讓它再做其他什么操作使得結(jié)果改變*/if(step==0) return;//解環(huán)的預(yù)先操作,先讓當(dāng)前環(huán)前面的環(huán)滿足解環(huán)的條件if(pos[num-1]==0) turn(num-1);/*↑【注意】↑看這里!如果pos數(shù)組0~9存狀態(tài),當(dāng)num=0就要單獨(dú)列出操作了,因?yàn)閿?shù)組會(huì)溢出*/for(int i=num-2;i>=1;i--){if(pos[i]==1) turn(i);}/*預(yù)先操作過(guò)程中最后一步如果step已經(jīng)到達(dá)0,返回到上一層就會(huì)一直往下執(zhí)行,這樣step就會(huì)變成負(fù)數(shù)了。這樣可不行!趕緊把它return掉!>A<*/if(step==0) return;//解開(kāi)當(dāng)前環(huán),并記錄步數(shù)pos[num]=(pos[num]+1)%2;step--; }int main() {//讀入題目給出的步數(shù)scanf("%d",&step);//從后往前解環(huán)for(int i=9;i>=1;i--){turn(i);}//輸出答案/*(step==0) 步數(shù)走完了(step>0) 步數(shù)沒(méi)走完*/if(step==0){for(int i=1;i<=9;i++){printf("%d",pos[i]);}putchar('\n');}else if(step>0){puts("-1");}return 0; }更新日志
2020-2-6
第一版完成!o( ̄▽ ̄)ブ
只寫(xiě)了兩題是因?yàn)槲宜㈩}刷得少orz
遞歸好難啊好難啊,想破腦殼殼QAQ
但是遞歸好神奇,本小白對(duì)它充滿喜歡,我一定要把遞歸這只小妖精抱緊緊!!!
總結(jié)
以上是生活随笔為你收集整理的【算法基础】经典例题说递归的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我使用火狐浏览器的理由
- 下一篇: 计算机网络职业技能,职业学校计算机网络专