2.1 遞歸

Chapter2: 時(shí)間復(fù)雜度分析、遞歸、查找與排序

1. 遞歸

1. 什么是遞歸

表現(xiàn):

在函數(shù)內(nèi)部調(diào)用函數(shù)本身

應(yīng)用遞歸的問題特征

一個(gè)問題能夠劃分為更小規(guī)模的子問題求解笔喉,通過求解更小規(guī)模的子問題最終得到問題的答案铝耻。更具體地來說祠汇,即在本次函數(shù)調(diào)用中執(zhí)行一小部分任務(wù),然后傳遞一個(gè)變化了的參數(shù)蛋褥,調(diào)用該函數(shù)本身執(zhí)行接下來的任務(wù)。

組成:

  • 函數(shù)內(nèi)的自我調(diào)用
  • 出口條件

設(shè)計(jì)經(jīng)驗(yàn):

  • 找重復(fù)(子問題)

  • 找重復(fù)中的變化量(參數(shù))

  • 找參數(shù)的變化趨勢(shì)和邊界(出口)

    注意設(shè)計(jì)遞歸時(shí)睛驳,變化趨勢(shì)是大問題 >> 小問題烙心,函數(shù)執(zhí)行時(shí),變化趨勢(shì)是解決小問題 >> 解決大問題

練習(xí)策略:

  • 循環(huán)改遞歸
  • 了解經(jīng)典遞歸
  • 大量聯(lián)系乏沸,總結(jié)規(guī)律淫茵,掌握套路
  • 找到感覺,挑戰(zhàn)更高難度的遞歸(深度優(yōu)先蹬跃、廣度優(yōu)先等)

2. 簡(jiǎn)單的遞歸問題示例

2.1 單分支遞歸

使用遞歸求階乘

設(shè)計(jì)思路:

  • 找重復(fù): n! = n * (n-1)!, 求 (n-1)! 是原問題的重復(fù)(規(guī)模更小)
  • 找變化: 變化的量應(yīng)該作為參數(shù)
  • 找邊界: 即出口痘昌,n>=1

代碼:

int f(int n){
    if(n==1){
        return 1;
    }
    return n*f(n-1);
}
打印 [ i , j ] 之間的整數(shù)

即打印 i, i+1 ,..., j

當(dāng)然這個(gè)用一個(gè)循環(huán)就能解決,這里主要是為了講解遞歸的規(guī)律

設(shè)計(jì)思路:

  • 找重復(fù): 每次都是打印操作
  • 找變化: 第一次執(zhí)行打印 i , 第二次執(zhí)行打印 i+1 ,..., 直至 i>j
  • 找邊界: 即出口,從i開始辆苔,到j(luò)結(jié)束

代碼:

void printItoJ(int i,int j){
    if(i<j){
        return;
    }
    printf("%d ",i);
    printItoJ(i+1);
}
數(shù)組求和
  • 找重復(fù): 實(shí)際上是不斷地執(zhí)行求和操作
  • 找變化: 前邊的求和結(jié)果+下一個(gè)數(shù)算灸,下一個(gè)數(shù)的下標(biāo)在遞增
  • 找邊界: 直至數(shù)組最后一個(gè)元素,即i==arr.length-1為最后一次加法

求數(shù)組第 i 個(gè)元素到最后一個(gè)元素的和

int sumArray(int[] arr,int i){
    if(i==arr.length){
        return 0;
    }
    return arr[i]+sumArray[i+1]驻啤;
}
遞歸求最大公約數(shù)(輾轉(zhuǎn)相除法)
int gcd(int a,int b){
    if(a%b==0){
        return b;
    }
    return gcd(b,a%b);
}
遞歸進(jìn)行插入排序
插入排序原理示意
  1. 從第一個(gè)元素開始菲驴,該元素可以認(rèn)為已經(jīng)被排序
  2. 取出下一個(gè)元素,在已經(jīng)排序的元素序列中從后向前掃描
  3. 如果該元素(已排序)大于新元素骑冗,將該元素移到下一位置
  4. 重復(fù)步驟 3赊瞬,直到找到小于或等于新元素的已排序的元素
  5. 將新元素插入到該元素的位置后
  6. 重復(fù)步驟 2~5
插入排序原理示意
非遞歸的插入排序?qū)崿F(xiàn)

以從小到大的排序?yàn)槔?/p>

就是不斷把新元素與已排序元素從右到左進(jìn)行比較

  • 如果新元素比已排序元素大就不用挪動(dòng)
  • 如果新元素比已排序元素小
    • 將該新元素用 tmp 變量保存
    • 將已排序元素挨個(gè)往后挪,直至新元素比已排序元素大贼涩,將新元素插入到該元素后
/*按從小到大的插入排序巧涧,非遞歸 */
/*
C++中數(shù)組作為函數(shù)參數(shù)是傳址 
*/
void insSort(int arr[],int arrLen){

    //int arrLen = sizeof(arr)/sizeof(arr[0]); 不能在這里計(jì)算數(shù)組元素個(gè)數(shù),因?yàn)閭魅氲牟皇菙?shù)組而是其地址 
    for(int i=1;i<arrLen;i++){//第一個(gè)元素當(dāng)作已排列序列遥倦,從第二個(gè)元素開始排列 
        int tmp=arr[i];//未排序的最靠左的元素 
        int j=i-1;//已排序元的最靠后素的下標(biāo) 
        while(j>=0 && tmp<arr[j]){//當(dāng)新元素<已排序元素時(shí)谤绳,已排序元素挨個(gè)往后挪 
            arr[j+1]=arr[j];
            j--;            
        }
        arr[j+1]=tmp;//插入新元素 
    }
    //輸出排序后的數(shù)組 
    for(int i=0;i<arrLen;i++){
        printf("%d ",arr[i]);
    }
    printf("\n");
} 
遞歸的插入排序

問題的解決思路:

  • 找重復(fù):實(shí)質(zhì)就是 "新元素與已排序元素的比較,并在滿足條件的情況進(jìn)行按個(gè)的元素挪動(dòng)再插入新元素" 這一過程的不斷重復(fù)袒哥,隨著已排序元素?cái)?shù)量的增多缩筛,問題規(guī)模是在增大的。
  • 找變化:?jiǎn)栴}中每一次排序后堡称,已排序元素?cái)?shù)量+1
  • 找邊界:當(dāng)已排序元素等于數(shù)組元素時(shí)結(jié)束

然而設(shè)計(jì)遞歸問題時(shí)瞎抛,函數(shù)的調(diào)用方向應(yīng)該是問題規(guī)模不斷變小的,這樣函數(shù)執(zhí)行時(shí)才是 解決小問題>>解決大問題 的方向,這一點(diǎn)需要注意却紧。

遞歸形式的解決思路

  • 找重復(fù):對(duì) N 個(gè)元素進(jìn)行插入排序桐臊,可以分解為對(duì) N-1個(gè)元素進(jìn)行插入排序的結(jié)果與最后一個(gè)數(shù)進(jìn)行排序
  • 找變化:不斷深入遞歸,問題規(guī)模減小晓殊,層次越深断凶,插入排序函數(shù)的參數(shù)越小
  • 找邊界:直到下標(biāo)為0時(shí),不需要再進(jìn)行插入排序挺物,到達(dá)邊界懒浮,函數(shù)返回
代碼
void insSort2(int* arr,int i){
    
    if(i==0){//第1(下標(biāo)為0)個(gè)元素插入排序的結(jié)果 
        return;
    }
    
    insSort2(arr,i-1);//第i個(gè)元素的插入排序需要第i-1個(gè)元素的插入排序的結(jié)果 
    
    /*進(jìn)行第i個(gè)元素的插入排序*/
    int tmp=arr[i];//保存未排序的新元素 
    int j=i-1;//最靠右的已排序元素的下標(biāo) 
    while(j>=0 && tmp<arr[j]){//當(dāng)新元素小于已排序元素時(shí),不斷將已排序元素挨個(gè)往后挪 
        arr[j+1]=arr[j];
        j--;
    } 
    
    arr[j+1]=tmp;//插入新元素 
}

2.2 多分支遞歸

求斐波那契亞數(shù)列第N項(xiàng)

斐波那契亞數(shù)列

第n項(xiàng)=第n-1項(xiàng)+第n-2項(xiàng)

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

發(fā)現(xiàn)多分支遞歸會(huì)有很多重復(fù)項(xiàng)识藤,可以進(jìn)行優(yōu)化砚著,之后討論相關(guān)內(nèi)容

斐波那契亞多分支遞歸示意

參考資料

[1] 插入排序步驟講解與實(shí)例分析

[2] 插入排序圖文講解

[3] 2.1-2.5 遞歸與排序解法

[4] C語(yǔ)言通過指針訪問一維數(shù)組、二維數(shù)組痴昧、三維數(shù)組

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稽穆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赶撰,更是在濱河造成了極大的恐慌舌镶,老刑警劉巖柱彻,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異餐胀,居然都是意外死亡哟楷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門否灾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卖擅,“玉大人,你說我怎么就攤上這事墨技〕徒祝” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵扣汪,是天一觀的道長(zhǎng)断楷。 經(jīng)常有香客問我,道長(zhǎng)崭别,這世上最難降的妖魔是什么冬筒? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮紊遵,結(jié)果婚禮上账千,老公的妹妹穿的比我還像新娘侥蒙。我一直安慰自己暗膜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布鞭衩。 她就那樣靜靜地躺著学搜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪论衍。 梳的紋絲不亂的頭發(fā)上瑞佩,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音坯台,去河邊找鬼炬丸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蜒蕾,可吹牛的內(nèi)容都是我干的稠炬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼咪啡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼首启!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撤摸,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤毅桃,失蹤者是張志新(化名)和其女友劉穎褒纲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钥飞,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莺掠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了读宙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汁蝶。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖论悴,靈堂內(nèi)的尸體忽然破棺而出掖棉,到底是詐尸還是另有隱情,我是刑警寧澤膀估,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布幔亥,位于F島的核電站,受9級(jí)特大地震影響察纯,放射性物質(zhì)發(fā)生泄漏帕棉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一饼记、第九天 我趴在偏房一處隱蔽的房頂上張望香伴。 院中可真熱鬧,春花似錦具则、人聲如沸即纲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)低斋。三九已至,卻和暖如春匪凡,著一層夾襖步出監(jiān)牢的瞬間膊畴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工病游, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唇跨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓衬衬,卻偏偏與公主長(zhǎng)得像买猖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佣耐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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