算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列)
遞歸
遞歸在算法中具有很重要的地位,也是很多學(xué)習(xí)編程的初學(xué)者非常頭疼的問(wèn)題,看我的這篇文章,希望能為還處于迷霧中的你帶來(lái)希望
首先我們要知道遞歸的作用:
1.可替代多重循環(huán)
2.解決本來(lái)就是用遞歸形式定義的問(wèn)題
3.將問(wèn)題分解為規(guī)模更小的子問(wèn)題進(jìn)行求解
其實(shí)對(duì)于我來(lái)說(shuō),遞歸非常重要的原因在于可以替代多重循環(huán),當(dāng)循環(huán)過(guò)大時(shí),會(huì)給計(jì)算機(jī)相當(dāng)大的負(fù)荷,一時(shí)半會(huì)很難出結(jié)果,這時(shí)候我們就需要運(yùn)用到遞歸這個(gè)手段。這里我會(huì)把知識(shí)由淺入深,以舉例的方式讓各位登堂入室,話不多說(shuō),直接上干貨。
1.求階乘
遞歸的基本概念就是一個(gè)函數(shù)調(diào)用其自身
我們都知道如何求階乘,這是我們初中數(shù)學(xué)就學(xué)了的內(nèi)容,那么如何用編程的思維解決這個(gè)問(wèn)題呢
對(duì)于遞歸,我們首先要明確問(wèn)題是什么,把抽象事物具體化
接下來(lái)我們需要厘清遞歸中的表達(dá)式或者關(guān)系
然后找到遞歸結(jié)束條件,也就是遞歸出口——不遞歸的條件。如果沒(méi)有遞歸出口,那么函數(shù)只會(huì)像一個(gè)無(wú)底洞,結(jié)果大失所望
這是對(duì)應(yīng)的C代碼
其實(shí)遞歸很耗費(fèi)內(nèi)存資源的,因?yàn)槊看握{(diào)用一下自身就會(huì)需要?jiǎng)?chuàng)建一個(gè)新的棧來(lái)存放。
為了加深印象,我把程序改一下
具體函數(shù)調(diào)用分析如下圖
每次一個(gè)遞歸調(diào)用完后,所在的棧空間也會(huì)被立即釋放,得到的結(jié)果會(huì)返回上一個(gè)棧,直到函數(shù)結(jié)束
既然基本套路熟悉了,我想帶大家聊聊遞歸中很經(jīng)典的問(wèn)題,不用猜也知道,漢諾塔問(wèn)題
2.Hanoi問(wèn)題
這個(gè)問(wèn)題為什么說(shuō)是經(jīng)典呢,因?yàn)樗莻€(gè)簡(jiǎn)單又很精髓的遞歸問(wèn)題
通過(guò)該問(wèn)題,你可以知道遞歸還有一個(gè)重要特點(diǎn),就是把細(xì)節(jié)模塊化,以整體的思想看待問(wèn)題。
首先分情況討論,
1.原目標(biāo)塔上只有一個(gè)盤子,那么我們直接將盤子從A移到C即可
2.原目標(biāo)塔上有不止一個(gè)盤子,又由于大盤在下小盤在下的規(guī)定,我們可以先將A座的盤子都移到B座以C為中間“變量”,當(dāng)A座只剩最后一個(gè)盤子,也就是全場(chǎng)最大的盤子,將其移到C座,然后以A為中間“變量”,將B座盤子移到C座
具體C代碼如下
#include<stdio.h>void Hanoi(int n, char src, char mid, char dest) {if(n == 1){printf("%c -> %c \n", src, dest);}else{Hanoi(n - 1, src, dest, mid); // printf("%c -> %c \n", src, dest);Hanoi(1, src, mid, dest);Hanoi(n - 1, mid, src, dest);}}int main() {int n;printf("Please enter number : ");scanf("%d",&n);Hanoi(n, 'A', 'B', 'C');return 0; }對(duì)于遞歸,記住不要糾結(jié)具體細(xì)節(jié),把握整體框架最為重要!
這里還有一個(gè)小練習(xí),關(guān)于求最大公約數(shù)的問(wèn)題,比方說(shuō)求12和8的最大公約數(shù),你可以先想想,然后再看我的代碼答案,記得,要用遞歸做哦!
#include<stdio.h>int gcd(int a, int b) {if(b == 0)return a;elsereturn gcd(b, a % b); }int main() {printf("%d\n",gcd(12,8)); }是不是很簡(jiǎn)單,遞歸有的時(shí)候就有點(diǎn)像函數(shù)調(diào)用,它只是比較特殊,它只是總是在調(diào)用自己罷了。如果除數(shù)等于零,那么被除數(shù)就是最大公約數(shù),如果除數(shù)不等于零,那么就把b的值給a,b的值變?yōu)閍 % b。遞歸還有一個(gè)特點(diǎn),就是以十分簡(jiǎn)易的代碼實(shí)現(xiàn)了值的互換或者修改
3.N皇后問(wèn)題
N皇后問(wèn)題是八皇后問(wèn)題的延伸,要求在NxN格的國(guó)際象棋上擺放N個(gè)皇后,使其不能互相攻擊,即任意兩個(gè)皇后都不能處于同一行、同一列或同一斜線上,問(wèn)有多少種擺法。
#include<stdio.h> #include<math.h>int queenPos[100]; int N;void NQueen(int k) //在0~k-1行皇后擺好的情況下擺第k行的皇后 {int i;if(k == N){for(i = 0; i < N; i++)printf("%d ",queenPos[i] + 1);printf("\n");}else{for(i = 0; i < N; i++) //逐步嘗試第k個(gè)皇后的列位置 {int j;for(j = 0; j < k; j++){if(queenPos[j] == i || abs(k - j) == abs(queenPos[j] - i))break;}if(j == k){queenPos[k] = i;NQueen(k + 1);}}} } int main() {scanf("%d",&N);NQueen(0);return 0; }這里我們不妨設(shè)最多有100個(gè)皇后,定義數(shù)組queenPos[100],然后再設(shè)全局變量N用于記錄有多少皇后,NQueen(int k)是用于記錄在0~k-1行皇后擺好的情況下擺第k行的皇后。如果全都擺好了,即k == N,那么把可能擺放位置的其中一種輸出,如果棧都退完了那么就結(jié)束了,否則銷毀當(dāng)前這個(gè)棧再往回退棧。如果k != N,那么先循環(huán)遍歷N個(gè)可能的位置給第k行的皇后,然后再把暫時(shí)確定的第k行皇后的位置與前k-1行所有皇后比較,如果有產(chǎn)生沖突的則再改變第k行皇后的位置。如果j==k,那么就確定第k行皇后的位置,同時(shí)進(jìn)入下一層NQueen(k+1)。
這就是這個(gè)遞歸的思路
四皇后是N皇后最小的底線,大家可以試一試,答案是
4.逆波蘭表達(dá)式
逆波蘭表達(dá)式的定義:
1)一個(gè)數(shù)是一個(gè)逆波蘭表達(dá)式,值為該數(shù);
2)“運(yùn)算符 逆波蘭表達(dá)式 逆波蘭表達(dá)式”是逆波蘭表達(dá)式,值為兩個(gè)逆波蘭表達(dá)式的值運(yùn)算結(jié)果。
這里我不妨設(shè)整個(gè)表達(dá)式所占的大小不超過(guò)20個(gè)字符,代碼如下:
#include<stdio.h> #include<stdlib.h>double exp() {char s[20];scanf("%s",s);switch(s[0]){case '+' :return exp() + exp();case '-' :return exp() - exp();case '*' : return exp() * exp();case '/' : return exp() / exp();default : return atof(s);break;} }int main() {printf("%lf",exp());return 0; }atof()函數(shù)是stdlib.h中的函數(shù),將字符串內(nèi)容轉(zhuǎn)化成double類型。
當(dāng)輸入* + 1 2 - 8 6
結(jié)果為
5.爬樓梯
LiHua爬樓梯,每次可以走1級(jí)或者兩級(jí),要求輸入樓梯級(jí)數(shù),求不同的走法數(shù)
這里我們可以吧問(wèn)題拆分細(xì)小化
n級(jí)臺(tái)階的走法 = n - 1 級(jí)臺(tái)階走法 + n - 2 級(jí)臺(tái)階走法
例如共有5級(jí)臺(tái)階,可拆分成第一步走一級(jí)和第一步走兩級(jí)的走法,然后再?gòu)牡谝徊阶咭患?jí)再細(xì)分之后的臺(tái)階走法。
C代碼如下:
這里還有一個(gè)類似爬樓梯的題目,可以參照這篇博客2020 計(jì)蒜客藍(lán)橋杯省賽 B 組模擬賽(一)題解2.爬樓梯
6.放蘋果
關(guān)于遞歸最重要的無(wú)非是遞歸表達(dá)式和遞歸終止條件。
首先分析,需要輸入蘋果數(shù)和盤子數(shù)。關(guān)于兩者的數(shù)目就存在兩種情況:
1、蘋果數(shù)n大于盤子數(shù)m,n > m時(shí),f(n,m) = f(m,m);
2、蘋果數(shù)n小于等于盤子數(shù)m,n <= m時(shí),f(n,m) = f(n, m - 1) + f(n - m,m),即有盤子為空的放法 + 無(wú)盤子為空放法。
詳細(xì)代碼如下:
#include<stdio.h>int f(int n, int m) {if(n < m)return f(n, n);if(n == 0)return 0;if(m == 0)return 1;elsereturn f(n, m - 1) + f(n - m, m); }int main() {int t, n, m, i;scanf("%d", &t);for(i = 0; i < t; i++){scanf("%d %d", &n, &m);printf("%d\n", f(n, m));}return 0; }三個(gè)蘋果三個(gè)盤子就是4
經(jīng)過(guò)這么多練習(xí)可以發(fā)現(xiàn),遞歸很多時(shí)候都用于直接就出結(jié)果,不在意輸出過(guò)程的題目。
7.全排列
全排列是遞歸中非常重要的知識(shí)點(diǎn),在深度學(xué)習(xí)中的用處很廣。比方說(shuō)我對(duì)1、2、3、4、5進(jìn)行全排列,那么如下圖所示
首先讓第一個(gè)位置有n種擺法,然后就只用考慮p+1到q的全排列了,把一個(gè)大的全排列分解為一層層嵌套的子問(wèn)題這便是遞歸思想,注意最后還要把交換過(guò)的元素?fù)Q回來(lái),要不然會(huì)使一些情況重復(fù)。
詳細(xì)代碼如下
這里還有一個(gè)類似的全排列題型,可以參看博文藍(lán)橋杯2015第六屆C語(yǔ)言B組省賽習(xí)題題解——習(xí)題E.九數(shù)組分?jǐn)?shù)
這里我列出九數(shù)組分?jǐn)?shù)的代碼,你們可以觀察一下,思路其實(shí)都是一樣的,先swap然后遞歸再swap換回來(lái)。
如果喜歡我的文章,請(qǐng)記得三連哦,點(diǎn)贊關(guān)注收藏,你的每一個(gè)贊每一份關(guān)注每一次收藏都將是我前進(jìn)路上的無(wú)限動(dòng)力 !!!↖(▔▽▔)↗感謝支持,下期更精彩!!!
總結(jié)
以上是生活随笔為你收集整理的算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python拼图_用python的PIL
- 下一篇: 移动定向流量怎么用?怎么开通物联卡定向流