C——函數遞歸

在 C 語言里,函數遞歸是一種函數調用自身的編程技術。下面開始逐一介紹。

一、什么是遞歸?

遞歸其實是?種解決問題的?法,在C語?中,遞歸就是函數??調???。

寫?個史上最簡單的C語?遞歸代碼:
#include <stdio.h>
int main()
{
?? ?printf("hehe\n");
?? ?main();//main函數中?調?了main函數
?? ?return 0;
}
上述就是?個簡單的遞歸程序,只不過上?的遞歸只是為了演?遞歸的基本形式,不是為了解決問
題,代碼最終也會陷?死遞歸,導致棧溢出(Stack overflow)。
1.遞歸的思想:
把?個?型復雜問題層層轉化為?個與原問題相似,但規模較?的?問題來求解;直到?問題不能再被拆分,遞歸就結束了。所以遞歸的思考?式就是把?事化?的過程。
遞歸中的遞就是遞推的意思,歸就是回歸的意思。
2.遞歸的限制條件
遞歸在書寫的時候,有2個必要條件:
遞歸存在限制條件,當滿?這個限制條件的時候,遞歸便不再繼續。
每次遞歸調?之后越來越接近這個限制條件。
二、遞歸舉例
1.求n的階乘
?個正整數的階乘(factorial)是所有?于及等于該數的正整數的積,并且0的階乘為1。
?然數n的階乘寫作 n!。
題?:計算n的階乘(不考慮溢出),n的階乘就是1~n的數字累積相乘。
1.1代碼的分析和實現
n的階乘的公式: n! = n ? (n ? 1)!
舉例:
5!= 5 * 4 * 3 * 2 * 1
4!= 4 * 3 * 2 * 1
所以:5!= 5 * 4!
從這個公式不難看出:如何把?個較?的問題,轉換為?個與原問題相似,但規模較?的問題來求解的。
n的階乘和n-1的階乘是相似的問題,但是規模要少了n。有?種有特殊情況是:當 n==0 的時候,n的階乘是1,?其余n的階乘都是可以通過上?的公式計算。
這樣就可以寫出n的階乘的遞歸公式:
那我們就可以寫出函數Fact求n的階乘,假設Fact(n)就是求n的階乘,那么Fact(n-1)就是求n-1的階
乘,函數如下:
int Fact(int n)
{
?? ?if (n == 0)
?? ??? ?return 1;
?? ?else
?? ??? ?return n * Fact(n - 1);
}
測試:
#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;
}
結果:
(這里不考慮n太大的情況,n太大容易溢出)
2.畫圖演練
2.1舉例
順序打印?個整數的每?位
輸??個整數m,按照順序打印整數的每?位。
?如:
輸?:1234 輸出:1 2 3 4
輸?:125 輸出:1 2 5
2.2代碼的分析和實現
拿到這個題目,我們需要想的第一件事就是如何得到每一位數。
如果n是?位數,n的每?位就是n??
n是超過1位數的話,就得拆分每?位
1234%10就能得到4,然后1234/10得到123,這就相當于去掉了4
然后繼續對123%10,就得到了3,再除10去掉3,以此類推
不斷的 %10 和 /10 操作,直到1234的每?位都得到;
但是這?有個問題就是得到的數字順序是倒著的
但是這可以發現,最后一位數是最容易得到的,通過%10就可以得到,那我們假設想寫?個函數Print來打印n的每?位,如下表?:

Print(n)
如果n是1234,那表?為
Print(1234) //打印1234的每?位
其中1234中的4可以通過 % 10得到,那么
Print(1234)就可以拆分為兩步:
1. Print(1234 / 10) //打印123的每?位
2. printf(1234 % 10) //打印4
完成上述2步,那就完成了1234每?位的打印
那么Print(123)?可以拆分為Print(123 / 10) + printf(123 % 10)

以此類推下去,就有
Print(1234)
==> Print(123) + printf(4)
==> Print(12) + printf(3)
==> Print(1) + printf(2)
==> printf(1)

直到被打印的數字變成?位數的時候,就不需要再拆分,遞歸結束。
那么代碼完成也就?較清楚:
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;
}

輸入和輸出的結果:

在這里,我們運用到了大事化小的思路。

把Print(1234) 打印1234每?位,拆解為?先Print(123)打印123的每?位,再打印得到的4
把Print(123) 打印123每?位,拆解為?先Print(12)打印12的每?位,再打印得到的3
直到Print打印的是?位數,直接打印就?。
2.3畫圖演練
以1234每一位的打印來推演一下

三、遞歸與迭代

遞歸是?種很好的編程技巧,但是和很多技巧?樣,也是可能被誤?的,就像舉例1?樣,看到推導的公式,很容易就被寫成遞歸的形式:
int Fact(int n)
{
?? ?if (n == 0)
?? ??? ?return 1;
?? ?else
?? ??? ?return n * Fact(n - 1);
}
Fact函數是可以產?正確的結果,但是在遞歸函數調?的過程中涉及?些運?時的開銷。
在C語?中每?次函數調?,都需要為本次函數調?在內存的棧區,申請?塊內存空間來保存函數調?期間的各種局部變量的值,這塊空間被稱為運?時堆棧,或者函數棧幀。
函數不返回,函數對應的棧幀空間就?直占?,所以如果函數調?中存在遞歸調?的話,每?次遞歸函數調?都會開辟屬于??的棧幀空間,直到函數遞歸不再繼續,開始回歸,才逐層釋放棧幀空間。
所以如果采?函數遞歸的?式完成代碼,遞歸層次太深,就會浪費太多的棧幀空間,也可能引起棧溢出的問題。
所以如果不想使?遞歸,就得想其他的辦法,通常就是迭代的?式(通常就是循環的?式)。
?如:計算 n 的階乘,也是可以產?1~n的數字累計乘在?起的。
1.舉例
求第n個斐波那契數
以計算 factorial(5) 為例,執行過程如下:
factorial(5) 調用 5 * factorial(4)。
factorial(4) 調用 4 * factorial(3)。
factorial(3) 調用 3 * factorial(2)。
factorial(2) 調用 2 * factorial(1)。
factorial(1) 滿足基本情況,返回 1。
然后逐步回溯計算:
factorial(2) 返回 2 * 1 = 2。
factorial(3) 返回 3 * 2 = 6。
factorial(4) 返回 4 * 6 = 24。
factorial(5) 返回 5 * 24 = 120。
由此可以看出,這里不適合使用遞歸,用迭代的方式效率更高。
遞歸的優缺點
優點:
代碼簡潔:遞歸可以讓代碼更加簡潔、易讀,尤其是處理一些具有遞歸結構的問題,如樹的遍歷、分治算法等。
易于理解:對于某些問題,遞歸的思路更貼合人類的思維方式,更容易設計和實現。
缺點:
性能問題:遞歸調用會頻繁地進行函數調用和返回操作,帶來額外的開銷,可能會導致性能下降。
棧溢出風險:如果遞歸深度過大,會導致棧空間耗盡,引發棧溢出錯誤。
注意事項
要確保遞歸函數包含基本情況,防止無限遞歸。
注意遞歸深度,避免棧溢出。在必要時,可以考慮使用迭代(循環)來替代遞歸。
迭代是指基于一個初始值,按照特定的規則反復執行一系列操作,每一次執行都會對前一次的結果進行更新,直到滿足某個終止條件為止。這個過程可以看作是對一個狀態不斷進行修改和演進的循環過程。
遞歸和迭代對比
執行效率:
迭代:通常情況下,迭代的執行效率相對較高,因為它避免了函數調用棧的頻繁操作,只是在循環結構內進行簡單的指令重復執行,減少了額外的開銷。
遞歸:遞歸由于不斷地進行函數調用和返回,會有一定的時間和空間開銷,尤其是在遞歸深度較大時,可能會出現棧溢出等性能問題。
代碼可讀性:
迭代:對于一些簡單邏輯,迭代代碼的結構清晰,易于理解和調試,直接通過循環條件和循環體就能明白代碼的執行流程。
遞歸:遞歸代碼在處理具有遞歸結構的問題(如樹的遍歷、分治算法等)時,代碼可能更簡潔,更符合人們對問題的分解式思考方式,但對于初學者或者復雜邏輯,理解起來可能有一定難度。

迭代和遞歸各有優缺點,在實際編程中,需要根據問題的特點、性能要求和代碼的可讀性來選擇合適的方法。有時候,迭代和遞歸可以相互轉換,通過合理的設計和優化,可以提高代碼的性能和可維護性。

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

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

相關文章

IdeaVim配置指南

一、什么是 IdeaVim&#xff1f; IdeaVim 是 JetBrains 系列 IDE&#xff08;如 IntelliJ IDEA, WebStorm, PyCharm 等&#xff09;中的一個插件&#xff0c;讓你在 IDE 里使用 Vim 的按鍵習慣&#xff0c;大大提升效率。 安裝方法&#xff1a; 在 IDE 中打開 設置(Settings) →…

Notepad++中XML格式化插件介紹

Notepad++中XML格式化插件介紹 背景安裝指南安裝步驟驗證安裝成功安裝失敗可嘗試使用說明XML文件格式正確時格式化錯誤格式檢查XML Tools插件核心功能盤點常見問題格式化后沒變化中文顯示亂碼拯救雜亂XML格式!Notepad++這個神器插件,必須接收!背景 接手別人寫的XML,縮進亂成…

自動化創業機器人:現狀、挑戰與Y Combinator的啟示

自動化創業機器人&#xff1a;現狀、挑戰與Y Combinator的啟示 前言 AI驅動的自動化創業機器人&#xff0c;正逐步從科幻走向現實。我們設想的未來是&#xff1a;商業分析、PRD、系統設計、代碼實現、測試、運營&#xff0c;全部可以在monorepo中由AI和人類Co-founder協作完成…

第1章 算法設計基礎

1-1 什么是算法 見識算法 算法是計算機科學的基石&#xff1a;從古代算術到現代計算機&#xff0c;算法始終是解決問題的核心。 算法的起源 張蒼《九章算術》&#xff1a;創立了機械化算法體系&#xff08;如“合分術”分數相加算法&#xff09;。 歐幾里得《幾何原本》&am…

java中ArrayList擴容機制的解析

本文將系統地介紹 Java 中 ArrayList 的擴容機制&#xff0c;包括其初始容量的設置、觸發擴容的時機、容量增長算法、擴容的詳細流程以及性能優化建議&#xff0c;幫助讀者從源碼層面深入理解這一關鍵特性&#xff0c;并在實際開發中合理預分配容量以提升性能。 一、ArrayList…

【網絡服務器】——回聲服務器(echo)

作用 實現回聲服務器的客戶端/服務器程序&#xff0c;客戶端通過網絡連接到服務器&#xff0c;并發送任意一串英文信息&#xff0c;服務器端接收信息后&#xff0c;執行數據處理函數&#xff1a;將每個字符轉換為大寫并回送給客戶端顯示。 客戶端&#xff1a;發送字符信息 服…

智能學習空間的范式革新:基于AI驅動的自習室系統架構與應用研究

摘要 在 “互聯網 + 教育” 深度融合的背景下,傳統自習室面臨個性化服務缺失、學習效率低下等瓶頸。本文提出一種基于人工智能技術的 AI 自習室系統架構,通過構建多模態數據感知、個性化學習引擎及智能環境調控模塊,實現學習過程的精準化、智能化與沉浸式體驗。研究結合計算…

HTML01:HTML基本結構

HTML基本結構 <html> <head><meta charset"UTF-8"><title>我的第一個網頁</title> </head> <body>我的第一個網頁 </body> </html><body、</body等成對的標簽&#xff0c;分別叫開發標簽和閉合標簽單獨…

Spring Boot 實現多種來源的 Zip 多層目錄打包下載(本地文件HTTP混合)

需要將一批文件&#xff08;可能分布在不同目錄、不同來源&#xff09;打包成Zip格式&#xff0c;按目錄結構導出給用戶下載。 1. 核心思路 支持將本地服務器上的文件&#xff08;如/data/upload/xxx.jpg&#xff09;打包進Zip&#xff0c;保持原有目錄結構。支持通過HTTP下載…

【Elasticsearch】在kibana中能獲取已創建的api keys嗎?

在 Kibana 中&#xff0c;目前沒有直接的界面功能可以列出或查看已創建的 API 密鑰&#xff08;API keys&#xff09;。API 密鑰的管理和查看主要通過 Elasticsearch 的 REST API 來完成&#xff0c;而不是通過 Kibana 的管理界面。 在 Kibana 中使用 Dev Tools 查看 API 密鑰…

公司項目架構搭建者

公司項目架構搭建者分析 項目架構搭建的核心角色 #mermaid-svg-FzOOhBwW3tctx2AR {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FzOOhBwW3tctx2AR .error-icon{fill:#552222;}#mermaid-svg-FzOOhBwW3tctx2AR .err…

《技術馴化情感:AI伴侶、監控與倫理框架的重構挑戰》

技術滲透與情感異化機制 情感計算技術正通過多種核心算法和數據處理方法深入人類生活&#xff0c;其在重構人類情感關系的同時也潛藏情感異化風險。本節從生物特征捕捉、行為模式誘導和認知框架重塑三方面解析情感計算的技術機理&#xff0c;并探討其導致的情感依賴現象。 生物…

32單片機——獨立看門狗

1、IWDG的簡介 IWDG&#xff1a;Independent watchdog&#xff0c;即獨立看門狗 獨立看門狗本質上是一個定時器&#xff0c;該定時器是一個12位的遞減計數器&#xff0c;當計數器的值減到0的時候&#xff0c;就會產生一個復位信號 如果在計數沒減到0之前&#xff0c;重置計數器…

[計算機網絡]數據鏈路層

408考綱(數鏈層部分): 0 概論&#xff1a;數據鏈路層都干什么事&#xff0c;提供啥功能 比物理層再高一層就是數據鏈路層&#xff0c;咱們上一篇講物理層&#xff0c;物理層直接接觸傳輸介質&#xff0c;現在數據鏈路層是使用物理層的傳輸服務&#xff0c;然后實現更多的功能。…

OpenAI大變革!繼續與微軟等,以非營利模式沖擊AGI

今天凌晨2點&#xff0c;OpenAI宣布&#xff0c;將繼續由非營利組織控制&#xff1b;現有的營利性實體將轉變為一家公共利益公司&#xff1b;非營利組織將控制該公共利益公司&#xff0c;并成為其重要的持股方。 這也就是說OpenAI曾在去年提到的由非營利性轉變成營利性公司&am…

庫存怎么管?怎樣才能做到有效的庫存管理?

說到庫存管理&#xff0c;估計大多數老板和管理者都有過“煩心事”。一方面&#xff0c;庫存過多&#xff0c;貨物堆積如山&#xff0c;堆在倉庫里也不動&#xff0c;結果占地方還占用資金&#xff1b;另一方面&#xff0c;又有可能遇到客戶急著要貨&#xff0c;可是庫存卻緊張…

Kotlin-空值和空類型

變量除了能引用一個具體的值之外,還有一種特殊的值,那就是 null, 它代表空值, 也就是不引用任何對象 在Kotlin中, 對空值的處理是非常嚴格的,正常情況下,我們的變量是不能直接賦值為 null 的,否則無法編譯通過, 這直接在編譯階段就避免了空指針問題 Kotlin中所有的類型默認都是…

[特殊字符]算法次元突破:螺旋矩陣的“能量解碼術” vs 超立方體的“維度折疊指南”

&#x1f50d; 引言 如果科幻電影中的能量矩陣是算法的考題&#xff0c;你會用螺旋指針破解它的DNA嗎&#xff1f; 如果《星際穿越》的五維空間變成編程題&#xff0c;你敢用動態規劃丈量時間的褶皺嗎&#xff1f; 今天&#xff0c;我們將化身算法世界的能量解…

高光譜相機賦能煙葉分選:精準、高效與智能化的新突破

煙草產業作為中國重要的經濟支柱&#xff0c;煙葉分選的質量與效率直接影響行業效益。傳統人工分選存在效率低、主觀性強、標準難以統一等問題&#xff0c;而機器視覺技術受限于可見光波段&#xff0c;難以捕捉煙葉深層特征。深圳中達瑞和科技有限公司推出的高光譜相機解決方案…

矩陣求導常用公式解析:標量、向量與矩陣的導數計算

矩陣求導常用公式解析&#xff1a;標量、向量與矩陣的導數計算 矩陣求導常用公式解析&#xff1a;標量、向量與矩陣的導數計算矩陣求導的布局問題1. 分子布局 vs 分母布局對比表2. 布局沖突的典型場景分析3. 混合布局的兼容性處理 一、標量對向量求導1. 線性函數求導2. 二次型函…