国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院

首頁 > 編程 > C > 正文

對C語言中遞歸算法的深入解析

2020-01-26 15:59:57
字體:
供稿:網(wǎng)友

許多教科書都把計算機階乘和菲波那契數(shù)列用來說明遞歸,非常不幸我們可愛的著名的老潭老師的《C語言程序設(shè)計》一書中就是從階乘的計算開始的函數(shù)遞歸。導致讀過這本經(jīng)書的同學們,看到階乘計算第一個想法就是遞歸。但是在階乘的計算里,遞歸并沒有提供任何優(yōu)越之處。在菲波那契數(shù)列中,它的效率更是低的非常恐怖。

這里有一個簡單的程序,可用于說明遞歸。程序的目的是把一個整數(shù)從二進制形式轉(zhuǎn)換為可打印的字符形式。例如:給出一個值4267,我們需要依次產(chǎn)生字符‘4',‘2',‘6',和‘7'。就如在printf函數(shù)中使用了%d格式碼,它就會執(zhí)行類似處理。

我們采用的策略是把這個值反復除以10,并打印各個余數(shù)。例如,4267除10的余數(shù)是7,但是我們不能直接打印這個余數(shù)。我們需要打印的是機器字符集中表示數(shù)字‘7'的值。在ASCII碼中,字符‘7'的值是55,所以我們需要在余數(shù)上加上48來獲得正確的字符,但是,使用字符常量而不是整型常量可以提高程序的可移植性。‘0'的ASCII碼是48,所以我們用余數(shù)加上‘0',所以有下面的關(guān)系:

          ‘0'+ 0 =‘0'
          ‘0'+ 1 =‘1'
          ‘0'+ 2 =‘2'
             ...
從這些關(guān)系中,我們很容易看出在余數(shù)上加上‘0'就可以產(chǎn)生對應(yīng)字符的代碼。接著就打印出余數(shù)。下一步再取商的值,4267/10等于426。然后用這個值重復上述步驟。

這種處理方法存在的唯一問題是它產(chǎn)生的數(shù)字次序正好相反,它們是逆向打印的。所以在我們的程序中使用遞歸來修正這個問題。

我們這個程序中的函數(shù)是遞歸性質(zhì)的,因為它包含了一個對自身的調(diào)用。乍一看,函數(shù)似乎永遠不會終止。當函數(shù)調(diào)用時,它將調(diào)用自身,第2次調(diào)用還將調(diào)用自身,以此類推,似乎永遠調(diào)用下去。這也是我們在剛接觸遞歸時最想不明白的事情。但是,事實上并不會出現(xiàn)這種情況。

這個程序的遞歸實現(xiàn)了某種類型的螺旋狀while循環(huán)。while循環(huán)在循環(huán)體每次執(zhí)行時必須取得某種進展,逐步迫近循環(huán)終止條件。遞歸函數(shù)也是如此,它在每次遞歸調(diào)用后必須越來越接近某種限制條件。當遞歸函數(shù)符合這個限制條件時,它便不在調(diào)用自身。

在程序中,遞歸函數(shù)的限制條件就是變量quotient為零。在每次遞歸調(diào)用之前,我們都把quotient除以10,所以每遞歸調(diào)用一次,它的值就越來越接近零。當它最終變成零時,遞歸便告終止。

/*接受一個整型值(無符號0,把它轉(zhuǎn)換為字符并打印它,前導零被刪除*/

復制代碼 代碼如下:

#include <stdio.h>

int binary_to_ascii( unsigned int value)
{
          unsigned int quotient;

     quotient = value / 10;
     if( quotient != 0)
           binary_to_ascii( quotient);
     putchar ( value % 10 + '0' );
}


遞歸是如何幫助我們以正確的順序打印這些字符呢?下面是這個函數(shù)的工作流程。
1. 將參數(shù)值除以10
2. 如果quotient的值為非零,調(diào)用binary-to-ascii打印quotient當前值的各位數(shù)字
3. 接著,打印步驟1中除法運算的余數(shù)

注意在第2個步驟中,我們需要打印的是quotient當前值的各位數(shù)字。我們所面臨的問題和最初的問題完全相同,只是變量quotient的值變小了。我們用剛剛編寫的函數(shù)(把整數(shù)轉(zhuǎn)換為各個數(shù)字字符并打印出來)來解決這個問題。由于quotient的值越來越小,所以遞歸最終會終止。

一旦你理解了遞歸,閱讀遞歸函數(shù)最容易的方法不是糾纏于它的執(zhí)行過程,而是相信遞歸函數(shù)會順利完成它的任務(wù)。如果你的每個步驟正確無誤,你的限制條件設(shè)置正確,并且每次調(diào)用之后更接近限制條件,遞歸函數(shù)總是能正確的完成任務(wù)。

但是,為了理解遞歸的工作原理,你需要追蹤遞歸調(diào)用的執(zhí)行過程,所以讓我們來進行這項工作。追蹤一個遞歸函數(shù)的執(zhí)行過程的關(guān)鍵是理解函數(shù)中所聲明的變量是如何存儲的。當函數(shù)被調(diào)用時,它的變量的空間是創(chuàng)建于運行時堆棧上的。以前調(diào)用的函數(shù)的變量扔保留在堆棧上,但他們被新函數(shù)的變量所掩蓋,因此是不能被訪問的。

當遞歸函數(shù)調(diào)用自身時,情況于是如此。每進行一次新的調(diào)用,都將創(chuàng)建一批變量,他們將掩蓋遞歸函數(shù)前一次調(diào)用所創(chuàng)建的變量。當我追蹤一個遞歸函數(shù)的執(zhí)行過程時,必須把分數(shù)不同次調(diào)用的變量區(qū)分開來,以避免混淆。

程序中的函數(shù)有兩個變量:參數(shù)value和局部變量quotient。下面的一些圖顯示了堆棧的狀態(tài),當前可以訪問的變量位于棧頂。所有其他調(diào)用的變量飾以灰色的陰影,表示他們不能被當前正在執(zhí)行的函數(shù)訪問。

假定我們以4267這個值調(diào)用遞歸函數(shù)。當函數(shù)剛開始執(zhí)行時,堆棧的內(nèi)容如下圖所示:


執(zhí)行除法之后,堆棧的內(nèi)容如下:


  
接著,if語句判斷出quotient的值非零,所以對該函數(shù)執(zhí)行遞歸調(diào)用。當這個函數(shù)第二次被調(diào)用之初,堆棧的內(nèi)容如下:
 
堆棧上創(chuàng)建了一批新的變量,隱藏了前面的那批變量,除非當前這次遞歸調(diào)用返回,否則他們是不能被訪問的。再次執(zhí)行除法運算之后,堆棧的內(nèi)容如下:

quotient的值現(xiàn)在為42,仍然非零,所以需要繼續(xù)執(zhí)行遞歸調(diào)用,并再創(chuàng)建一批變量。在執(zhí)行完這次調(diào)用的出發(fā)運算之后,堆棧的內(nèi)容如下:
 
此時,quotient的值還是非零,仍然需要執(zhí)行遞歸調(diào)用。在執(zhí)行除法運算之后,堆棧的內(nèi)容如下:



不算遞歸調(diào)用語句本身,到目前為止所執(zhí)行的語句只是除法運算以及對quotient的值進行測試。由于遞歸調(diào)用這些語句重復執(zhí)行,所以它的效果類似循環(huán):當quotient的值非零時,把它的值作為初始值重新開始循環(huán)。但是,遞歸調(diào)用將會保存一些信息(這點與循環(huán)不同),也就好是保存在堆棧中的變量值。這些信息很快就會變得非常重要。

現(xiàn)在quotient的值變成了零,遞歸函數(shù)便不再調(diào)用自身,而是開始打印輸出。然后函數(shù)返回,并開始銷毀堆棧上的變量值。

每次調(diào)用putchar得到變量value的最后一個數(shù)字,方法是對value進行模10取余運算,其結(jié)果是一個0到9之間的整數(shù)。把它與字符常量‘0'相加,其結(jié)果便是對應(yīng)于這個數(shù)字的ASCII字符,然后把這個字符打印出來。

輸出4:


接著函數(shù)返回,它的變量從堆棧中銷毀。接著,遞歸函數(shù)的前一次調(diào)用重新繼續(xù)執(zhí)行,她所使用的是自己的變量,他們現(xiàn)在位于堆棧的頂部。因為它的value值是42,所以調(diào)用putchar后打印出來的數(shù)字是2。

輸出42:

接著遞歸函數(shù)的這次調(diào)用也返回,它的變量也被銷毀,此時位于堆棧頂部的是遞歸函數(shù)再前一次調(diào)用的變量。遞歸調(diào)用從這個位置繼續(xù)執(zhí)行,這次打印的數(shù)字是6。在這次調(diào)用返回之前,堆棧的內(nèi)容如下:

輸出426:


現(xiàn)在我們已經(jīng)展開了整個遞歸過程,并回到該函數(shù)最初的調(diào)用。這次調(diào)用打印出數(shù)字7,也就是它的value參數(shù)除10的余數(shù)。

輸出4267:
然后,這個遞歸函數(shù)就徹底返回到其他函數(shù)調(diào)用它的地點。
如果你把打印出來的字符一個接一個排在一起,出現(xiàn)在打印機或屏幕上,你將看到正確的值:4267
使用遞歸一定要有跳出的條件:
這是一個最簡單的遞歸, 不過它會一直執(zhí)行, 可用 Ctrl+C 終止.
復制代碼 代碼如下:

#include <stdio.h>
void prn(int num) {
    printf("%d/n", num);
    if (num > 0) prn(--num); 
}
int main(void)
{
    prn(9);
    getchar();
    return 0;
}

復制代碼 代碼如下:

實例: 翻轉(zhuǎn)字符串
#include <stdio.h>
void revers(char *cs);
int main(void)
{
    revers("123456789");
    getchar();   
    return 0;
}
void revers(char *cs)
{
    if (*cs)
    {
        revers(cs + 1);
        putchar(*cs);
    }
}

復制代碼 代碼如下:

實例: 階乘
#include <stdio.h>
int factorial(int num);
int main(void)
{
    int i;
    for (i = 1; i <= 9; i++)
    printf("%d: %d/n", i, factorial(i));
    getchar();   
    return 0;
}
int factorial(int num)
{
    if (num == 1)
        return(1);
    else
        return(num * factorial(num-1));
}

復制代碼 代碼如下:

實例: 整數(shù)到二進制
#include <stdio.h>
void IntToBinary(unsigned num);
int main(void)
{
    IntToBinary(255); /* 11111111 */
    getchar();
    return 0;
}
void IntToBinary(unsigned num) {
    int i = num % 2;
    if (num > 1) IntToBinary(num / 2);
    putchar(i ? '1' : '0');
//    putchar('0' + i);  /* 可代替上面一句 */
}

復制代碼 代碼如下:

剖析遞歸:
#include <stdio.h>
void prn(unsigned n);
int main(void)
{
    prn(1);
    getchar();
    return 0;
}
void prn(unsigned n) {
    printf("%d: %p/n", n, &n);  /* A */
    if (n < 4)
        prn(n+1);               /* B */
    printf("%d: %p/n", n, &n);  /* C */
}

例輸出效果圖:

分析:
程序運行到 A, 輸出了第一行.
此時 n=1, 滿足 < 4 的條件, 繼續(xù)執(zhí)行 B 開始了自調(diào)用(接著會輸出第二行); 注意 n=1 時語句 C 還有待執(zhí)行.
...如此循環(huán), 一直到 n=4, A 可以執(zhí)行, 但因不滿足條件 B 執(zhí)行不了了; 終于在 n=4 時得以執(zhí)行 C.
但此時內(nèi)存中有四個函數(shù)都等待返回(分別是 n=1、2、3、4 時), 咱們分別叫它 f1、f2、f3、f4.
f4 執(zhí)行 C 輸出了第五行, 函數(shù)返回, 返回給 f3(此時 n=3), f3 得以繼續(xù)執(zhí)行 C, 輸出了第六行.
f3 -> f2 -> 繼續(xù) C, 輸出了第七行.
f2 -> f1 -> 繼續(xù) C, 輸出了第八行, 執(zhí)行完畢!

如此看來, 遞歸函數(shù)還是很費內(nèi)存的(有時不如直接使用循環(huán)), 但的確很巧妙.

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表

圖片精選

国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院
日本电影在线观看| 国产三级av在线| 中文字幕4区| 99热国产在线| gogogo影视剧免费观看在线观看| 国产欧美在线观看视频| 国产高清大尺度一区二区不卡| 国产精品186在线观看在线播放| 国产精品免费麻豆入口| 97高清视频| 亚洲精品手机在线| 高清色视频在线观看| 国产精品视频福利一区二区 | 在线国产1区| 欧美日韩**字幕一区| 国产亚洲精品一区二区在线观看| 国产三级视频在线看| baoyu777.永久免费视频| 国产美女在线看| av中文天堂在线| 国产网站av| 国产麻豆精品视频一区二区| 在线国产网址| 国产不卡视频| 狠狠色丁香婷婷| jizz亚洲大全| 在线久久视频| 国产91足控脚交在线观看| 免费看av大片| www.大网伊人| 青青青国产视频| www久久日com| 青青草视频在线免费观看| 精品女厕厕露p撒尿| 国产精品入口麻豆免费| 中文字幕第一页在线| 国产免费av在线| 黄色电影网站在线观看| 香蕉视频在线观看www| 在线天堂中文www视软件| 91精选福利| 97在线免费| 国产精品爱久久久久久久小说| 午夜在线小视频| 中文字幕在线观看播放| 激情六月婷婷| 成网站在线观看人免费| 黄色毛片在线看| 中中文字幕av在线| 国产天堂在线观看| 国产黄色在线网站| 国产成人福利| sese在线视频| 国产精选在线视频拍拍拍| 国产一区在线视频观看| 中文字幕色视频| 2019中文字幕在线电影免费| 国产视频一二| 一本大道久久精品| 国产在线色视频| 国产视频精选在线| 精品全国在线一区二区| 亚洲精品视频区| 九九在线免费视频| 国产精品免费视频二三区| 夜夜嗨yeyeh| 最新av免费看| а√资源新版在线天堂| 国产精品你懂的在线观看| a级片国产精品自在拍在线播放| 国产在线www| av在线播放网| 午夜av电影| sese一区| av在线播放国产| 国产偷倩在线播放| 黄色片免费在线| аⅴ成人天堂中文在线| 国产99在线|亚洲| 天天爱天天做色综合| 日本在线免费中文字幕| 国产精品18久久久久网站| 超碰在线观看免费| 中文字幕av免费| 2019年中文字幕| 国产亚洲精品一区二区在线观看| xxxxx中文字幕| 亚洲网站一区| 国产素人视频在线观看| 国产中文字幕在线看| 操操操综合网| 日本不卡影院| a视频免费看| 四虎久久影院| 夜夜爽视频导航| 在线观看午夜av| 在线视频观看你懂的| 国产黄色在线| 国产日产一区二区| 国产精品白浆视频免费观看| 国产丝袜精品丝袜| 国产高潮av| 精品欧美不卡一区二区在线观看| 高清欧美精品xxxxx在线看| 午夜在线视频播放| 羞羞视频在线观看免费| 国产三级视频在线看| 国产欧美久久久久久久久| 国产区成人精品视频| 2021天堂中文幕一二区在线观| 国产精品免费视频二三区| 国产精品国精产品一二| 91在线网站| 欧美专区日韩| 国产一级免费看| 亚洲欧美精品日韩欧美| 日本一本久久| 天天爱天天做色综合| 尤物在线网址| 影音av资源站| 懂色av中文在线| 91美女主播在线视频| 日本高清不卡中文字幕 | 在线视频三级| jizz亚洲| 国产极品嫩模在线视频一区| av网站在线播放| 国产免费网址| 午夜影院在线| 国产一级大片| 美女av在线播放| 国产一二区在线观看| 四虎一区二区三区| 国产精品四虎| 可以免费看污视频的网站| 国产精品免费91| 狠狠插狠狠操| 日本综合一区二区三区| 国产日本在线| 国产中文第一页| 天天插天天狠天天透| 国产三区四区在线观看| 青青草免费在线视频| 在线色视频网| 国产精品久久久久久福利| 欧美亚洲系列| 2018中文字幕在线| 国产三级视频在线| 午夜视频在线看| 久久er视频| 日本高清中文字幕在线| 精品麻豆一区二区三区| 国产卡一卡二卡三| 欧美96在线| 日本aⅴ写真网站免费| 99reav| 国产对白国语对白| 国产精品视频一区二区三区麻豆 | 精品国产免费观看一区| 国产鲁鲁视频在线观看特色| 青青久草在线| 久热精品视频在线播放| a视频免费看| 本道综合精品| 欧美色第一页| 国产中文字幕在线| 国产永久免费| 国产色视频网站| 国产国产国产国产国产国产| 国产成人无吗| 亚洲激情丁香| www在线观看播放免费视频日本| 碰草在线视频| 国产美女在线一区二区三区| 91超碰国产在线| 国产三级视频在线| 99久久免费精品国产免费| 亚洲欧美综合乱码精品成人网| 亚洲图区欧美| 久久亚洲资源| 最好看更新中文字幕| 国产91在线视频蝌蚪| jlzzjlzz欧美大全| 国产农村一级特黄α**毛片| 国产精品入口麻豆免费看| 精品卡一卡卡2卡3网站| 亚洲xxxxxx| 福利在线国产| 在线午夜影院| 国产精品一区二区婷婷| 中文视频在线| 国产九色视频| av在线免费播放网站| 最新中文字幕在线视频| 亚洲精品在线播放视频| 最新中文字幕av专区| 亚洲国产aⅴ精品| 国产日本韩国在线播放| jizz亚洲| 中文字幕av免费|