c語言的簡易教法—— 函數遞歸

文章目錄

  • 一、什么是遞歸?
    • 1.1遞歸的思想
    • 1.2遞歸的限制條件
  • 二、遞歸案例
    • 2.1 案例1:求n的階層
      • 2.1.1分析
      • 2.1.2 遞歸函數(Fact)的代碼實現
      • 2.1.3 測試:main函數實現
      • 2.1.4 運行結果和畫圖推演
      • 2.1.5 擴展:迭代方法求解n的階乘
    • 2.2 案例2:順序打印?個整數的每?位
      • 2.2.1分析
      • 2.2.2打印數(print)的每一位代碼實現
      • 2.2.3 測試:print函數實現
      • 2.2.4 運行結果和畫圖推演
    • 2.3 案例3:求第n個斐波那契數
      • 2.3.1分析
      • 2.3.2求第n個斐波那契數(fib) 的代碼實現
      • 2.3.3 測試:fib函數實現
      • 2.2.4 運行結果和畫圖推演
      • 2.1.5 擴展:迭代方法求解斐波那契數
    • 2.4 案例4:遞歸實現n的k次方
      • 2.4.1分析
      • 2.4.2 mypow函數實現
      • 2.4.3 測試:主函數實現mypow函數
      • 2.4.4 運行結果
  • 總結


一、什么是遞歸?

在代碼運行中,有時候我們碰到冗長的代碼無法進行下筆進行編碼,這時候我們將會學習到應用函數遞歸進行運算。那么什么是遞歸呢?

1.1遞歸的思想

什么是函數遞歸?函數遞歸就是把大事化小,把小事在進行化小,直到解決問題。函數遞歸主要分為兩個過程一個叫遞推,一個叫回歸。

遞推主要是將大事簡化成一個個小事逐漸往下進行,回歸就是把最小的事情解決然后逐漸傳遞給上一個值進行回歸計算值,直到返回最初需要解決的問題。

1.2遞歸的限制條件

遞歸存在兩種限制條件 :

1.遞歸存在限制條件, 遞推不能無限一直遞推下去,即遞推存在限制條件:什么時候進入遞歸,遞歸什么時候結束,遞歸存在進入遞歸的條件和遞歸的結束條件。只有滿足這個限制條件,遞歸將不會繼續遞歸下去。

為什么函數遞歸不能無限遞歸的原因

在這里插入圖片描述

在這里我簡單寫了一個遞歸函數,內存中分為幾個不同的區域,在函數運行中產生的局部變量都會存放在內存中的棧區,接著我們看到函數,首先進入主函數main,然后我們會創建一個棧幀空間存儲main函數中的變量,然后代碼繼續運行下去我們調用test函數,調用test函數會開辟一個棧幀空間,在test函數中再次進行調用test函數就會出現如上圖一樣的情況,內存中的棧區也是有一定空間的,每次調用函數都會額外開辟一份空間,如果一直無限調用下去棧區將會溢出。

2.每次遞歸調?之后越來越接近這個限制條件

因為每次遞歸,相當于都是一次新的函數調用,而每次函數調用系統必須給該函數劃分棧幀空間,內部的遞歸函數沒有退出,上層的遞歸就不能退出,棧幀就會累積許多塊,如果累積超過棧的總大小,就會棧溢出。所以函數遞歸每次都需要逐漸的接近這個函數遞歸的停止條件,否則他就會無限一直遞歸下去。


提示:以下是本篇文章代碼部分,下面案例可供參考

二、遞歸案例

2.1 案例1:求n的階層

?個正整數的階乘(factorial)是所有?于及等于該數的正整數的積,并且0的階乘為1。
?然數n的階乘寫作n!。

題?:計算n的階乘(不考慮溢出),n的階乘就是1~n的數字累積相乘。

2.1.1分析

在這里插入圖片描述

上面分析圖解中,我們就要有遞歸的思想:把一個較大的事情轉化為一個與原問題相似,但規模較小的問題進行求解。

當n==0的時候,n的階乘是1,其余的階乘都是可以用如上圖一樣的規律可以推出來的。

遞歸公式

2.1.2 遞歸函數(Fact)的代碼實現

int Fact(int n)
{if(n==0)return 1;elsereturn n*Fact(n-1);
}

2.1.3 測試:main函數實現

#include <stdio.h>
int Fact(int n)  
{if(n==0)  return 1;  else  return n*Fact(n-1);  
}
int main()  
{int n = 0;  scanf("%d", &n);  int ret = Fact(n);  printf("%d\n", ret);  return 0;  
}

2.1.4 運行結果和畫圖推演

注意此處不考慮n太大,n太大會存在溢出的情況,因為這是一個整型變量,存儲的最大值65530
在這里插入圖片描述

在這里插入圖片描述

2.1.5 擴展:迭代方法求解n的階乘

#include<stdio.h>int Fact(int n)
{int i = 0;int sum = 1;for (i = 1; i <= n; i++){sum = sum * i;}return sum;
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fact(n);>   printf("%d\n", ret);   return 0;   
}

2.2 案例2:順序打印?個整數的每?位

題目:輸??個整數m,打印這個按照順序打印整數的每?位。
例如:

輸入1234,打印1 2 3 4
輸入4578,打印4 5 7 8

2.2.1分析

首先看到打印的第一步,我們想到的是怎么求出該數的每一位:
如果該數是一位數,那我們直接打印這一位即可。
如果該數超過一位數,那我們就得一個個求出該數的每一位。

假如我們要打印1234的每一位,我們得先求出他的每一位

1234 % 10 = 4,在這里我們得到了個位上的數4。 1234 / 10 = 123;
123 % 10 = 3, 在這里我們得到了十位上的數3。 123 / 10 = 12;
12 % 10 = 2,在這里我們得到了百位上的數2。 12 / 10 =1;
1 % 10 = 1 ,此時一位數我們直接打印即可。1 / 10 =0;由這里可以判斷結束遞歸的條件是該數大于9.
但是這里發現最先得到的是個位上的數,因此我們這里弄出一個函數print
print(1234) = print(123) + printf(4);= print(1234/10) + printf(1234%10)
print(123) = print(12) + printf(3) + printf(4) ; = print(123/10) + printf(123%10)
print(12) = printf(1) + printf(2)+ printf(3) + printf(4) ;

2.2.2打印數(print)的每一位代碼實現

void Print(int n) voidPrint(國際)       
{if(n>9) 如果(n>9{Print(n/10); 打印(n/10;       }printf("%d ", n%10); printf(“%d ”,n%10;       
}

2.2.3 測試:print函數實現

#include<stdio.h>
void Print(int n)  
{if(n>9)  {Print(n/10);  }printf("%d ", n%10);  
}
int main()
{int m = 0;scanf("%d", &m);Print(m);return 0;
}

2.2.4 運行結果和畫圖推演

在這里插入圖片描述
在這里插入圖片描述

2.3 案例3:求第n個斐波那契數

題目:求第n個斐波那契數;

輸入: 5 輸出:5
輸入:10 輸出:55

2.3.1分析

有不知道什么叫斐波那契數列的同學可以百度搜索詳細了解一下,在這里小編就簡單給大家介紹一下什么叫斐波那契數列。斐波那契數第n個數等于前兩個數之和的相加,因此斐波那契數列第一第二個數都為1,以此推導剩下的斐波那契數:
1 1 2 3 5 8 13 21 34 55 。。。。
因此我們可以推導出求第n個斐波那契數的公式為:
在這里插入圖片描述

2.3.2求第n個斐波那契數(fib) 的代碼實現

int fib(int n)
{if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}

2.3.3 測試:fib函數實現

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

2.2.4 運行結果和畫圖推演

在這里插入圖片描述

在這里插入圖片描述

2.1.5 擴展:迭代方法求解斐波那契數

在上面細心的人就會發現我們求解第50個斐波那契數,電腦突然間瘋狂的轉起來,但是始終等了很久也沒有得到第50個斐波那契數的值,這是為什么呢?

在這里插入圖片描述

由上面的圖我們可以看出,當我們在進行遞歸運算的時候,他會重復計算很多的重復值,例如我們上面再遞歸求fib(49)需要求fib(48)和fib(47);而我們求fib(48)又會求一次fib(47)和fib(46),這時隨著遞歸的層次原來越深,我們會發現我們會重復計算很多次重復的值,接下來我們計算一下算fib(3)一共計算了多少次。

#include<stdio.h>
int count = 0;
int fib(int n)
{if (n == 3)count++;if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}
int main()
{int m = 0;scanf("%d", &m);int ret = fib(m);printf("%d\n",ret);printf("count = %d",count);return 0;
}

在這里插入圖片描述

這時我們可以發現fib(3)重復計算了39088619次,這大大加大了計算機運行的難度,因此求解斐波那契數的時候遞歸求解是一個錯誤的選擇。因此我們可以直接采用迭代的求法。

#include<stdio.h>
int fib(int n)
{int a = 1; //開始時第一個斐波那契數int b = 1; //開始時第二個斐波那契數int c = 1; //返回求解的斐波那契數,因為如果n<=2返回1所以c的初始值為1while (n > 2){c = a + b;a = b; b = c; n--; }return c; 
}
int main() 
{int m = 0; scanf("%d", &m); int ret = fib(m); printf("%d\n",ret); return 0; 
}

2.4 案例4:遞歸實現n的k次方

模擬實現pow函數實現求解n的k次方

輸入:2 3 輸出:8
輸入:3 3 輸出:27

2.4.1分析

當k的值為0的時候,返回1;
當k的值為1的時候,返回n;
當k的值大于1的時候,返回n*mypow(k-1);
綜上:
在這里插入圖片描述

2.4.2 mypow函數實現

int mypower(int k, int n)
{if (n == 0)return 1;else if(n >= 1)return k * mypower(k, n - 1);
}

2.4.3 測試:主函數實現mypow函數

int mypower(int k, int n)
{if (n == 0)return 1;else if(n >= 1)return k * mypower(k, n - 1);
}
int main()
{int k = 0;int n = 0;scanf("%d%d", &k, &n);int ret = mypower(k, n); printf("%d ", ret); return 0;  }

2.4.4 運行結果

在這里插入圖片描述


總結

通過上面案例我們初步了解了函數遞歸的思想 :把大事化小的特點。明白函數遞歸的充要條件是1,首先要有限制條件,即進入遞歸的條件和結束遞歸的條件。2,在函數遞歸的時候我們要逐漸的接近這個遞歸條件。
當然函數遞歸的學習遠不如于此,需要大家在不斷的實踐練習中不斷了解函數遞歸的妙用。這是小編對于函數遞歸的理解如果有讀者有看不懂的地方或者更好的建議歡迎評論下方留言。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44038.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44038.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44038.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【66個開源+44個閉源Agent項目】

開源AI?Agent 1.AgentGPT 基于瀏覽器的 AutoGPT 實現&#xff0c;可通過無代碼平臺訪問。https://agentgpt.reworkd.ai/zh 2.AI Legion 一個讓智能體協同工作的平臺&#xff0c;其類似于 AutoGPT 和 Baby AGI&#xff0c;但用 TypeScript 編寫。https://github.com/eumemi…

如何使用BERT進行下游任務 - Transformer教程

BERT&#xff0c;即Bidirectional Encoder Representations from Transformers&#xff0c;是谷歌于2018年發布的預訓練語言模型。BERT的出現標志著自然語言處理領域的一個重要里程碑&#xff0c;因為它大幅提高了多種語言任務的性能。本文將詳細介紹如何使用BERT進行下游任務&…

華為如何做成數字化轉型?

目錄 企業數字化轉型是什么&#xff1f; 華為如何定義數字化轉型&#xff1f; 為什么做數字化轉型&#xff1f; 怎么做數字化轉型&#xff1f; 華為IPD的最佳實踐之“金蝶云” 企業數字化轉型是什么&#xff1f; 先看一下案例&#xff0c;華為經歷了多次戰略轉型&#xf…

前端工程化:Webpack配置全攻略

前端工程化&#xff1a;Webpack配置全攻略 前端小伙伴們&#xff0c;今天我們來聊聊那個讓人又愛又恨的 Webpack。沒錯&#xff0c;就是那個配置起來讓你想砸鍵盤&#xff0c;但又離不開它的構建工具。別擔心&#xff0c;跟著我來&#xff0c;保證讓你從 Webpack 小白變成配置…

人臉識別與檢測(保姆級教程--附帶源碼)

人臉識別與檢測&#xff08;保姆級教程–附帶源碼&#xff09; 項目背景 因項目需要招聘了一些日結工人&#xff0c;因此需要對工地現場的工人進行考勤管理&#xff0c;但工地只有海康攝像頭沒有專業考勤設備&#xff0c;因此需要基于視頻流開發人臉識別與檢測功能&#xff1…

Windows 虛擬機服務器項目部署

目錄 一、部署JDK下載JDK安裝JDK1.雙擊 jdk.exe 安裝程序2.點擊【下一步】3.默認安裝位置&#xff0c;點擊【下一步】4.等待提取安裝程序5.默認安裝位置&#xff0c;點擊【下一步】6.等待安裝7.安裝成功&#xff0c;點擊【關閉】 二、部署TomcatTomcat主要特點包括&#xff1a;…

奇怪的錯誤記錄

https://github.com/meta-llama/llama3/issues/80 讀模型沒問題&#xff0c;推理時出現&#xff1a; RuntimeError: “triu_tril_cuda_template” not implemented for ‘BFloat16’ ———————————————— 事發原因 我嘗試了解transformers的AutoProcessor時&a…

感應觸摸芯片集成為MCU,深度應用觸控按鍵技術的VR眼鏡

VR&#xff08;Virtual Reality&#xff09;即虛擬現實&#xff0c;簡稱VR&#xff0c;其具體內涵是綜合利用計算機圖形系統和各種現實及控制等接口設備&#xff0c;在計算機上生成的、可交互的三維環境中提供沉浸感覺的技術。它的工作原理是將左右眼圖像交互顯示在屏幕上的方式…

技術速遞|宣布為 .NET 升級助手提供第三方 API 和包映射支持

作者&#xff1a;Marco Goertz 排版&#xff1a;Alan Wang .NET 升級助手是一個 Visual Studio 擴展和命令行工具&#xff0c;可幫助您將應用從之前的 .NET 和 .NET Framework 升級到最新版本的 .NET。正如我們在之前的文章中所描述的那樣&#xff0c;它為升級 Microsoft 庫和框…

技術總結(1)——方向與成長思考

不知不覺已經發了30篇技術博客&#xff0c;本來最開始想的是回顧自己的技術生涯&#xff0c;怎樣做到失敗的生涯&#xff0c;但是后面發現&#xff0c;開始逐步寫技術博客&#xff0c;慢慢的開始沉浸里面這種回顧技術的感覺。做技術的人通常不喜歡研究市場&#xff0c;而做市場…

模型剪枝知識點整理

模型剪枝知識點整理 剪枝是深度學習模型優化的兩種常見技術&#xff0c;用于減少模型復雜度和提升推理速度&#xff0c;適用于資源受限的環境。 剪枝&#xff08;Pruning&#xff09; 剪枝是一種通過移除模型中不重要或冗余的參數來減少模型大小和計算量的方法。剪枝通常分為…

編程是學什么:探索編程世界的四大核心領域

編程是學什么&#xff1a;探索編程世界的四大核心領域 在數字化時代的浪潮中&#xff0c;編程已成為一項重要的技能。但很多人對于編程的學習內容仍然感到困惑&#xff0c;那么&#xff0c;編程究竟是學什么呢&#xff1f;本文將從四個方面、五個方面、六個方面和七個方面&…

探索TASKCTL和 DataStage 的ETL任務調度協同

在復雜多變的企業環境中&#xff0c;高效、準確的數據處理是支撐業務決策與運營的核心。本文將深入探討任務調度平臺TASKCTL與ETL工具DataStage的深度融合&#xff0c;通過詳盡的代碼示例、結合細節以及實際案例的具體描述&#xff0c;展示這兩個工具如何攜手打造企業數據處理生…

Xcode構建設置自定義:打造個性化的編譯環境

標題&#xff1a;Xcode構建設置自定義&#xff1a;打造個性化的編譯環境 在軟件開發過程中&#xff0c;根據不同的開發階段和需求&#xff0c;經常需要調整編譯設置以優化構建過程。Xcode作為蘋果官方的集成開發環境&#xff08;IDE&#xff09;&#xff0c;提供了豐富的自定義…

簡述 Java 內存模型(JMM),特別是堆與棧的區別?

Java內存模型&#xff08;JMM&#xff09;是Java平臺定義的一種多線程之間的通信規范&#xff0c;它確保了在不同的線程之間能夠正確地共享和協調對內存的訪問。 JMM的關鍵目標是解決并發編程中的可見性、原子性和有序性問題。 簡單來說&#xff0c;它規定了如何在硬件內存、…

【C語言】 —— 預處理詳解(下)

【C語言】 —— 預處理詳解&#xff08;下&#xff09; 前言七、# 和 \##7.1 # 運算符7.2 ## 運算符 八、命名約定九、# u n d e f undef undef十、命令行定義十一、條件編譯11.1、單分支的條件編譯11.2、多分支的條件編譯11.3、判斷是否被定義11.4、嵌套指令 十二、頭文件的包…

淺層神經網絡示例

輸出層采用sigmoid激活&#xff0c;隱藏層采用tanh激活 import h5py import numpy as npfrom project_02.code.planar_utils import load_planar_dataset, plot_decision_boundarydef sigmoid(z):s 1 / (1 np.exp(-z))return sdef init_parameters(n_x, n_h, n_y):"&qu…

如何在 Objective-C 中實現多態性,并且它與其他面向對象編程語言的多態性實現有何差異?

在Objective-C中&#xff0c;多態性可以通過使用父類的指針來調用子類的方法來實現。具體來說&#xff0c;可以定義一個父類的指針&#xff0c;然后將子類的實例賦值給這個指針。這樣&#xff0c;即使使用父類的指針來調用方法&#xff0c;實際上會調用子類的方法。 需要注意的…

Day1每日編程題日記:數字統計、兩個數組的交集、點擊消除

前言&#xff1a;該篇用于記錄自看。曾回看昨天的做題代碼&#xff0c;竟然會覺得陌生&#xff0c;這竟然是我寫的&#xff0c;細細讀了一下&#xff0c;原來我當時是這么想的。因此我覺得記代碼沒有實際用處&#xff0c;重點是領悟了思想&#xff0c;這樣子代碼就在心中&#…

HashMap----源碼解讀

源碼分析&#xff1a; public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable 在類的開頭聲明了幾個常量&#xff0c;以下是較為重要的&#xff1a; /*** 定義初始容量大小為16*/ static final int DEFAULT_I…