程序員大神教你學(xué)C語言編程——遞歸函數(shù)

一、什么是遞歸函數(shù)

(1)遞歸函數(shù)即自調(diào)用函數(shù)宇立,在函數(shù)內(nèi)部直接或間接地自己調(diào)用自己踪宠,即函數(shù)的嵌套調(diào)用是函數(shù)本身。

從字面上來看遞歸妈嘹,遞即遞推柳琢,采用循環(huán)的思路來描述復(fù)雜問題的方法。在遞推階段每一個遞歸調(diào)用通過進(jìn)一步調(diào)用自己來記住這次遞歸過程,當(dāng)其中調(diào)用滿足終止條件時染厅,遞推結(jié)束痘绎。歸即回歸,函數(shù)調(diào)用已逆序的方式回歸肖粮,知道最初調(diào)用的函數(shù)返回為止孤页,此時遞歸過程結(jié)束。舉個例子:

(2)遞歸的基本原理

第一:每一級的函數(shù)調(diào)用都有自己的變量涩馆。

第二:每一次函數(shù)調(diào)用都會有一次返回行施。

第三:遞歸函數(shù)中,位于遞歸調(diào)用前的語句和各級被調(diào)用函數(shù)具有相同的執(zhí)行順序魂那。

第四:遞歸函數(shù)中蛾号,位于遞歸調(diào)用后的語句的執(zhí)行順序和各個被調(diào)用函數(shù)的順序相反。

第五:雖然每一級遞歸都有自己的變量涯雅,但是函數(shù)代碼并不會得到復(fù)制鲜结。

最后:遞歸函數(shù)中必須包含可以終止遞歸調(diào)用的語句。

(3)遞歸的優(yōu)缺點

其優(yōu)點在于為某些變成問題提供了最簡單的解決方法活逆,簡化了程序設(shè)計精刷。而缺點是一些遞歸算法會很快耗盡計算機(jī)的內(nèi)存資源。同時蔗候,使用遞歸的程序難于閱讀和維護(hù)怒允。

二、函數(shù)調(diào)用機(jī)制的說明

任何函數(shù)之間不能嵌套定義,調(diào)用函數(shù)與被調(diào)用函數(shù)之間相互獨立遞歸函數(shù)的概念用法與實例(彼此可以調(diào)用)。發(fā)生函數(shù)調(diào)用時刹衫,被調(diào)用函數(shù)中保護(hù)了調(diào)用函數(shù)的運行環(huán)境和返回地址,使得調(diào)用函數(shù)的狀態(tài)可以被調(diào)用函數(shù)運行返回完全恢復(fù)丽惶,而且該狀態(tài)與被調(diào)用函數(shù)無關(guān)。

被調(diào)用函數(shù)運行的代碼雖然是同一個函數(shù)的代碼體爬立,但由于調(diào)用點钾唬,調(diào)用時狀態(tài),返回點的不同懦尝,可以看作是函數(shù)的一個副本知纷,與調(diào)用函數(shù)的代碼無關(guān)壤圃,所以函數(shù)的代碼是獨立的陵霉。被調(diào)用函數(shù)運行的棧空間獨立于調(diào)用函數(shù)的椢樯空間踊挠,所以與調(diào)用函數(shù)之間的數(shù)據(jù)也是無關(guān)的。函數(shù)之間考參數(shù)傳遞和返回值來聯(lián)系,函數(shù)看作為黑盒效床。

這種機(jī)制決定了 C/C++ 允許函數(shù)遞歸調(diào)用睹酌。

上面這段話的意思可以理解為,遞歸函數(shù)調(diào)用自身函數(shù)的時候剩檀,可以看作調(diào)用的是調(diào)用別的函數(shù)憋沿。

再有,被調(diào)用函數(shù)運行的椈铮空間獨立于調(diào)用函數(shù)的椃模空間 這句話如何理解?

首先运嗜,我們先說一下棧的概念:

參看:遞歸函數(shù)的概念用法與實例

參看:C語言再學(xué)習(xí) -- 內(nèi)存管理

棧是一個后進(jìn)先出(LIFO)的壓入(push)和彈出(pop)式的數(shù)據(jù)結(jié)構(gòu)壶辜。在程序運行時,系統(tǒng)每次向棧中壓入一個對象担租,然后棧指針向下移動一個位置砸民。當(dāng)系統(tǒng)從棧中彈出一個對象時,最近進(jìn)棧的對象將被彈出奋救。然后棧指針向上移動一個位置岭参。程序員經(jīng)常利用棧這種數(shù)據(jù)結(jié)構(gòu)來處理那些最適合用后進(jìn)先出邏輯來描述的編程問題。這里討論的程序中的棧每個程序中都是存在的菠镇,它不需要程序員編寫代碼去維護(hù)冗荸,而是由運行時系統(tǒng)自動處理。所謂系統(tǒng)自動維護(hù)利耍,實際上就是編譯器所產(chǎn)生的程序代碼蚌本。盡管在程序中看不到他們,但是程序員應(yīng)該對此有所了解隘梨。

再看看程序中的棧是如何工作的程癌。當(dāng)一個函數(shù)(調(diào)用者)調(diào)用另一個函數(shù)(被調(diào)用者)時,運行系統(tǒng)將把調(diào)用者的所有實參和返回地址壓入棧中轴猎,棧指針將移到合適的位置來容納這些數(shù)據(jù)嵌莉。最后進(jìn)棧的是調(diào)用者的返回地址。當(dāng)被調(diào)用這開始執(zhí)行時捻脖,系統(tǒng)把被調(diào)用者的自變量壓入棧中锐峭,并把棧指針再向下移,以保證有足夠的空間存儲被調(diào)用這聲明的所有自變量可婶。當(dāng)調(diào)用這把實參壓入棧后沿癞,被調(diào)用者就在棧中以自變量的形式建立形參。被調(diào)用這內(nèi)部的其他自變量也是存放在棧中的矛渴。由于這些進(jìn)棧操作椎扬,棧指針已經(jīng)移動所有這些局部變量之下。但是被調(diào)用者記錄了它剛開始執(zhí)行時的初始棧指針,以它為參考蚕涤,用正或負(fù)的偏移量來訪問棧中的變量筐赔。當(dāng)被調(diào)用者準(zhǔn)備返回時,系統(tǒng)彈出棧中所有的自變量揖铜,這是棧指針移動了被調(diào)用者剛開始執(zhí)行時的位置茴丰。接著被調(diào)用者返回,系統(tǒng)從棧中彈出返回地址天吓,調(diào)用者就可以繼續(xù)執(zhí)行了较沪。當(dāng)調(diào)用者繼續(xù)執(zhí)行時,系統(tǒng)還將從棧中彈出調(diào)用者的實參失仁,于是棧指針回到了調(diào)用發(fā)生前的位置尸曼。

現(xiàn)在接著說遞歸:

上面有提到了函數(shù)調(diào)用機(jī)制。遞歸之所以能實現(xiàn)萄焦,是因為函數(shù)的每個執(zhí)行過程都在棧中有自己的形參和局部變量的拷貝控轿,這些拷貝和函數(shù)的其他執(zhí)行過程毫不相干。這種機(jī)制是當(dāng)代大多數(shù)程序設(shè)計語言實現(xiàn)子程序結(jié)構(gòu)的基礎(chǔ)拂封,是使得遞歸成為可能茬射。假定某個調(diào)用函數(shù)調(diào)用了一個被調(diào)用函數(shù),再假定被調(diào)用函數(shù)又反過來調(diào)用了調(diào)用函數(shù)冒签。這第二個調(diào)用就被稱為調(diào)用函數(shù)的遞歸在抛,因為它發(fā)生在調(diào)用函數(shù)的當(dāng)前執(zhí)行過程運行完畢之前。而且萧恕,因為這個原先的調(diào)用函數(shù)刚梭、現(xiàn)在的被調(diào)用函數(shù)在棧中較低的位置有它獨立的一組參數(shù)和自變量,原先的參數(shù)和變量將不受影響票唆,所以遞歸能正常工作朴读。程序遍歷執(zhí)行這些函數(shù)的過程就被稱為遞歸下降。

程序員需保證遞歸函數(shù)不會隨意改變靜態(tài)變量和全局變量的值走趋,以避免在遞歸下降過程中的上層函數(shù)出錯衅金。程序員還必須確保有一個終止條件來結(jié)束遞歸下降過程,并且返回到頂層簿煌〉ǎ看下面的例子:

這里有一個問題一定要注意,就是static int sum = 0;

有些人就不明白姨伟,為什么要使用 static 類型修飾符惩琉,為什么不使用 int sum=0;?如果使用 int sum=0; 這樣的語句授滓,在每次調(diào)用函數(shù)add()的時候琳水,sum的值都是賦值為0,也就是第一步雖然加了1上來般堆,可是第二次調(diào)用的時候在孝,sum又回到了0。我們前面說了淮摔,static能保證本次初始化的值是上次執(zhí)行后的值私沮,這樣也就保證了前面想加的結(jié)果不會丟失。如果你修改為int sum=0和橙,最后結(jié)果一定是最后結(jié)果是5而不是15仔燕。

上面的例子就很好的解釋了,被調(diào)用函數(shù)運行的椖д校空間獨立于調(diào)用函數(shù)的椢螅空間 這句話。

三办斑、遞歸調(diào)用的形式

遞歸函數(shù)有直接遞歸調(diào)用和間接調(diào)用兩種形式.

直接遞歸即在函數(shù)出現(xiàn)調(diào)用函數(shù)本身外恕,例如:

間接遞歸調(diào)用指函數(shù)中調(diào)用其他函數(shù),而該其他函數(shù)卻又調(diào)用了本函數(shù)乡翅。例如:

下面鳞疲,再擴(kuò)展一種遞歸,尾遞歸

當(dāng)遞歸調(diào)用是整個函數(shù)中最后執(zhí)行的語句且它的返回值不屬于表達(dá)式的一部分時蠕蚜,這個遞歸調(diào)用就是尾遞歸的尚洽。

上面的直接遞歸例子的返回值是 return num*func(num-1); 是一個表達(dá)式自然不是尾遞歸了,將其改為尾遞歸:

當(dāng)編譯器檢查到一個函數(shù)是尾遞歸的時候靶累,它就會覆蓋當(dāng)前活躍記錄而不是在棧中去創(chuàng)建一個新的腺毫,這樣就解決了普通遞歸函數(shù)占用棧空間過大的問題挣柬。遺憾的是拴曲,大多數(shù)編程語言沒有針對尾遞歸做優(yōu)化,所以凛忿,即使把函數(shù)改成尾遞歸方式澈灼,也會導(dǎo)致棧溢出。星辰就暫時把遞歸說到這店溢,下一篇的話繼續(xù)接上叁熔,內(nèi)容還是講遞歸吧,基于太多床牧,一下子寫太多的話荣回,各位看了也會累。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末戈咳,一起剝皮案震驚了整個濱河市心软,隨后出現(xiàn)的幾起案子壕吹,更是在濱河造成了極大的恐慌,老刑警劉巖删铃,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耳贬,死亡現(xiàn)場離奇詭異,居然都是意外死亡猎唁,警方通過查閱死者的電腦和手機(jī)咒劲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诫隅,“玉大人腐魂,你說我怎么就攤上這事≈鹞常” “怎么了蛔屹?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豁生。 經(jīng)常有香客問我判导,道長,這世上最難降的妖魔是什么沛硅? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任眼刃,我火速辦了婚禮,結(jié)果婚禮上摇肌,老公的妹妹穿的比我還像新娘擂红。我一直安慰自己,他們只是感情好围小,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布昵骤。 她就那樣靜靜地躺著,像睡著了一般肯适。 火紅的嫁衣襯著肌膚如雪变秦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天框舔,我揣著相機(jī)與錄音蹦玫,去河邊找鬼。 笑死刘绣,一個胖子當(dāng)著我的面吹牛樱溉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纬凤,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼福贞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了停士?” 一聲冷哼從身側(cè)響起挖帘,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤完丽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拇舀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逻族,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年你稚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朱躺。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡刁赖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出长搀,到底是詐尸還是另有隱情宇弛,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布源请,位于F島的核電站枪芒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谁尸。R本人自食惡果不足惜舅踪,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望良蛮。 院中可真熱鬧抽碌,春花似錦、人聲如沸决瞳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皮胡。三九已至痴颊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屡贺,已是汗流浹背蠢棱。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留甩栈,地道東北人裳扯。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像谤职,于是被迫代替她去往敵國和親饰豺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 原文地址:C語言函數(shù)調(diào)用棧(一)C語言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用允蜈。當(dāng)一個函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,598評論 1 19
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,770評論 0 38
  • mTabLayout就是tablayout對象冤吨。drawable自己隨便畫一個shape或者叫設(shè)計弄個圖蒿柳。效果如下:
    jiongge閱讀 2,762評論 0 0
  • 我覺得,貓是一種至陰之物漩蟆。就像西方總把體態(tài)純黑的貓視為厄運的象征垒探。有很多的婦女被巫女殺死,原因就是她們都養(yǎng)了一只黑...
    奶味蘿莉閱讀 539評論 0 0
  • 假裝是一棵樹 一棵只會落葉 不會開花的樹 一棵可以 讓樹葉起舞奏樂的樹 一棵喜歡聽故事 每年只聽一個 能聽一萬年的...
    天野丟閱讀 306評論 0 2