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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

算法--递归--走台阶问题(2种递归+递归改循环)

發布時間:2024/7/5 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法--递归--走台阶问题(2种递归+递归改循环) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 遞歸:
      • 注意事項:
    • 問題1
            • 思路
            • 1.遞歸代碼(未考慮重復計算問題)
            • 2.循環代碼
            • 3.遞歸代碼(避免重復計算問題)
            • 測試運行時間
    • 問題2

遞歸:

一個問題可以分解成若干子問題,且求解思路一樣,當到一定的情況下有終止條件,這樣的問題可以用遞歸方法求解

注意事項:

  • 遞歸調用深度太大,棧空間會耗盡溢出
  • 注意避免調用中某些值的重復計算(見以下代碼3)
  • 遞歸,頻繁調用函數,時間成本高(見以下代碼1)
  • 遞歸代碼可以改成循環代碼 (見以下代碼2)
  • 問題1

    給你 n 個臺階,你的最大步幅是2步,可以一次走1步,也可以一次走2步,問有多少種走法?

    思路
  • 假設總共走法為 f (n) ,我現在走1步,后面還有 n-1 步(其走法為 f (n-1) )
  • 我還可以,開始走2步,后面還有 n-2 步(其走法為 f(n-2) )
  • 那么遞推公式即: f (n) = f (n-1) + f (n-2)
  • 終止條件:f (1) = 1; f (2) = 2;
  • 1.遞歸代碼(未考慮重復計算問題)

    以下所有代碼原來采用 size_t 溢出,改用 unsigned long

    #include <iostream> using namespace std; unsigned long cal(unsigned long n) {if(n==1)return 1;else if(n==2)return 2;return cal(n-1)+cal(n-2); } int main() {size_t n;cout << "請輸入你要走的臺階數 n :" ;cin >> n;cout << "走臺階有 " << cal(n) << " 種方案。" << endl;return 0; }

    以上遞歸方法,在 n 比較小的時候運行時間較短
    輸入 n = 100 時,超過10s還沒出結果,我就終止程序了。以下改用循環。

    2.循環代碼
    #include <iostream> using namespace std; int main() //循環 {unsigned long n, step, nextStep = 2, nextnextStep = 1;cout << "請輸入你要走的臺階數 n :" ;cin >> n;if(n > 0){if(n == 1){step = 1;} else if(n == 2){step = 2;}else{for(int i = 2; i < n; ++i){step = nextStep + nextnextStep;nextnextStep = nextStep;nextStep = step;}}cout << "走臺階有 " << step << " 種方案。" << endl;}return 0; }

    輸入 n = 100 時,改用循環,眨眼間出結果。

    3.遞歸代碼(避免重復計算問題)

    代碼 1 中的 f(n), 比如 n = 5 時

    以下代碼,屏蔽多次計算重復的值

    #include <iostream> #include <iterator> #include <map> using namespace std; unsigned long cal(unsigned long n, map<unsigned long, unsigned long>& n_fn_map) {map<unsigned long,unsigned long>::iterator iter = n_fn_map.find(n); //查找key nif(iter != n_fn_map.end()){return iter->second; //如果找到了,就不必進行下面計算了,直接返回value}if(n==1){n_fn_map.insert(pair<unsigned long, unsigned long>(1,1)); //把f(1)存入映射return 1;}else if(n==2){n_fn_map.insert(pair<unsigned long, unsigned long>(2,2)); //把f(2)存入映射return 2;}else{size_t sum = cal(n-1,n_fn_map)+cal(n-2,n_fn_map); //遞歸調用函數n_fn_map.insert(pair<unsigned long, unsigned long>(n,sum)); //求得的f(n)存入映射,供后面查詢直接使用return sum;} } int main() //遞歸(帶避免重復計算fn的值功能) {size_t n;cout << "請輸入你要走的臺階數 n :" ;cin >> n;map<unsigned long, unsigned long> n_Fn; //n,f(n)的 k,v 容器cout << "走臺階有 " << cal(n,n_Fn) << " 種方案。" << endl;return 0; }

    輸入 n = 100,程序也是眨眼間出結果

    測試運行時間

    測試程序運行時間shell代碼:https://blog.csdn.net/qq_21201267/article/details/81840299

    問題2

    給你 n 個臺階,你的最大步幅是2步,可以一次走1步,也可以一次走2步,先邁左腳,要求最后到達時是右腳,問有多少種走法?

    解法1:模擬實際的行走,暴力搜索

    /**1. @description: 39個臺階,一次走1步或2步,左腳出發,要求右腳到達2. @author: michael ming3. @date: 2019/4/6 18:174. @modified by: */ #include <iostream> using namespace std; void recursion(const unsigned long &targetStairs, unsigned long steps, unsigned long stairsWalkAway, unsigned long &ways) { //暴力搜索,n很大時效果不好if(stairsWalkAway > targetStairs) //走過了,不做記錄return;else if(stairsWalkAway == targetStairs && steps%2 == 0) //正好走到,且步數為偶數(右腳到達){ways++; //記錄一種方案可以return;}else //沒走到,繼續遞歸{recursion(targetStairs, steps+1, stairsWalkAway+1, ways);recursion(targetStairs, steps+1, stairsWalkAway+2, ways);} } int main() {unsigned long stairs = 0, steps = 0, stairsWalkAway = 0, ways = 0;cout << "請輸入臺階個數:" << endl;cin >> stairs;recursion(stairs, steps, stairsWalkAway, ways);cout << "左腳出發,右腳到達的方案有:" << ways << " 種。" << endl;return 0; }

    解法2:遞推公式和之前一樣,結束條件變了

  • n = 2 時,不論什么情況,大家都只有1種可能,使得右腳到達,f (2) = 1
  • n = 1時,只剩1步了,如果已經走過了偶數步,那就是不可能右腳達到,f(1) = 0;如果已走過奇數步,那也只有1種可能,右腳到達,f(1) = 1
  • 由于 f(1) 是變化的,所以不能用上面問題1的代碼3那種方法存儲 f(n) 的值,因為其都與 f(1) 相關。所以當 n 比較大的時候還沒有找到好的解決辦法。
  • #include <iostream> #include <map> using namespace std; unsigned long cal(size_t n, size_t stepWalkAway) {if(n==1){if(stepWalkAway%2 == 0)return 0; //只剩1步了,如果走過了偶數步,那就是右腳達到,不可能了,0return 1;}else if(n==2) //n = 2 時,不論什么情況,大家都只有1種可能,使得右腳到達{return 1;}else{return cal(n-1,stepWalkAway+1)+cal(n-2,stepWalkAway+1); //遞歸調用函數} } int main() {size_t n, stepWalkAway = 0;cout << "請輸入你要走的臺階數 n :" ;cin >> n;cout << "左腳開走,右腳走到有 " << cal(n,stepWalkAway) << " 種方案。" << endl;return 0; }


    解法3:動態規劃,現在還不太明白
    見他人博客:
    https://blog.csdn.net/qq_40269087/article/details/80236102
    大概的思路是:自底向上

    • 用 left [ i ] 表示剩余 i 個臺階,左腳到達的可能方案, right [ i ] 表示右腳到達的方案
    • 邊界條件: left [ 1 ] = 1; left [ 2 ] = 1; right [ 1 ] = 0; right [ 2 ] = 1
    • 現在還有3個臺階到達,相當于在前面到達的方案中,退一步(1個臺階或者2個臺階),那么我在剩余3個臺階時,左腳到達 left [ 3 ] = right [ 2 ] + right [ 1 ] ;(右腳可以到達的方案,后退一步就變成了左腳到達的方案)
    • 同理 right [ 3 ] = left [ 2 ] + left [ 1 ]
    #include <iostream> using namespace std; unsigned long dynamicProgram(size_t N) {unsigned long left[N+1], right[N+1];left [1] = 1; left [2] = 1; right [1] = 0; right [2] = 1;for(size_t i = 3; i <= N; ++i){left[i] = right[i-1] + right[i-2];right[i] = left[i-1] + left[i-2];}return right[N]; //題目要求返回右腳到達方案 } int main() {size_t N;cout << "請輸入你要走的臺階數 n :" ;cin >> N;cout << "左腳開走,右腳走到有 " << dynamicProgram(N) << " 種方案。" << endl;return 0; }


    總結

    以上是生活随笔為你收集整理的算法--递归--走台阶问题(2种递归+递归改循环)的全部內容,希望文章能夠幫你解決所遇到的問題。

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