日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列)

發布時間:2024/3/13 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

遞歸

遞歸在算法中具有很重要的地位,也是很多學習編程的初學者非常頭疼的問題,看我的這篇文章,希望能為還處于迷霧中的你帶來希望

首先我們要知道遞歸的作用:

1.可替代多重循環

2.解決本來就是用遞歸形式定義的問題

3.將問題分解為規模更小的子問題進行求解

其實對于我來說,遞歸非常重要的原因在于可以替代多重循環,當循環過大時,會給計算機相當大的負荷,一時半會很難出結果,這時候我們就需要運用到遞歸這個手段。這里我會把知識由淺入深,以舉例的方式讓各位登堂入室,話不多說,直接上干貨。

1.求階乘

遞歸的基本概念就是一個函數調用其自身
我們都知道如何求階乘,這是我們初中數學就學了的內容,那么如何用編程的思維解決這個問題呢
對于遞歸,我們首先要明確問題是什么,把抽象事物具體化

接下來我們需要厘清遞歸中的表達式或者關系
然后找到遞歸結束條件,也就是遞歸出口——不遞歸的條件。如果沒有遞歸出口,那么函數只會像一個無底洞,結果大失所望
這是對應的C代碼

#include<stdio.h>int Factorial(int n) {if(n == 0)return 1;elsereturn n * Factorial(n - 1); }int main() {printf("%d\n",Factorial(5));return 0; }

其實遞歸很耗費內存資源的,因為每次調用一下自身就會需要創建一個新的棧來存放。
為了加深印象,我把程序改一下

#include<stdio.h>int f(int n) {if(n > 1)f(n - 1);printf("n = %d\n",n); }int main() {f(5);printf("%d\n",f(5)); //printf的值是遞歸函數最后一行的雙引號內的字符串的長度 return 0; }

具體函數調用分析如下圖

每次一個遞歸調用完后,所在的棧空間也會被立即釋放,得到的結果會返回上一個棧,直到函數結束
既然基本套路熟悉了,我想帶大家聊聊遞歸中很經典的問題,不用猜也知道,漢諾塔問題

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代碼如下:

#include<stdio.h>int N;int stairs(int n) {if(n < 0)return 0;if(n == 0)return 1;if(n == 1)return 1;if(n == 2)return 2;return stairs(n - 1) + stairs(n - 2); }int main() {scanf("%d", &N);printf("%d\n", stairs(N));return 0; }

這里還有一個類似爬樓梯的題目,可以參照這篇博客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的全排列了,把一個大的全排列分解為一層層嵌套的子問題這便是遞歸思想,注意最后還要把交換過的元素換回來,要不然會使一些情況重復。
詳細代碼如下

#include<stdio.h>void swap(int A[], int i, int j) {int temp = A[i];A[i] = A[j];A[j] = temp; } void printArray(int A[], int n) {int i;for(i = 0; i < n; i++){printf("%d ", A[i]);}printf("\n"); }void perm(int A[], int p, int q) {if(p == q)printArray(A, q + 1);else{int i;for(i = p; i <= q; i++){swap(A, p, i);perm(A, p + 1, q);swap(A, p, i);}} }int main() {int A[5] = {1, 2, 3, 4, 5};perm(A, 0, 4); }

這里還有一個類似的全排列題型,可以參看博文藍橋杯2015第六屆C語言B組省賽習題題解——習題E.九數組分數

這里我列出九數組分數的代碼,你們可以觀察一下,思路其實都是一樣的,先swap然后遞歸再swap換回來。

#include <stdio.h>void test(int x[]) {int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];if(a*3==b) printf("%d / %d\n", a, b); }void f(int x[], int k) {int i,t;if(k>=9){test(x);return;}for(i=k; i<9; i++){{t=x[k]; x[k]=x[i]; x[i]=t;}f(x,k+1);t=x[k]; x[k]=x[i]; x[i]=t; // 填空處} }int main() {int x[] = {1,2,3,4,5,6,7,8,9};f(x,0); return 0; }

如果喜歡我的文章,請記得三連哦,點贊關注收藏,你的每一個贊每一份關注每一次收藏都將是我前進路上的無限動力 !!!↖(▔▽▔)↗感謝支持,下期更精彩!!!

總結

以上是生活随笔為你收集整理的算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。