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