[算法系列]一:遞歸類問題 從斐波那契數(shù)列開始

遞歸類問題

遞歸類問題指后續(xù)步驟都是有基本步驟疊加而成的問題睬罗。一般問題設(shè)定為做某事有n種方法x,y旭斥。容达。。琉预,每次只能用其中一種董饰,次數(shù)不限,問共有多少種方法排列組合可以造成結(jié)果R圆米?

這類問題思想很經(jīng)典卒暂,可以把問題分解成多個本原問題,變成遞歸問題娄帖,再將遞歸優(yōu)化為非遞歸算法也祠,我們從斐波那契數(shù)列開始講

fibonacci數(shù)列:

f(x)=\begin{cases}0,& x=0\\1,& 0< x\leq 2\\ f(x-1)+f(x-2),& x>2 \end{cases}
可以理解為每次可以從x=0近速, x=1诈嘿, x=2三件事里挑一件做堪旧,其中結(jié)果加0會導(dǎo)致問題閉環(huán),有無窮解奖亚,故一般問題不會有這種條件淳梦。所以我們的問題x為正整數(shù)。

現(xiàn)在問題就變成了:一共做了q次事昔字,每次從x=1 和x=2里挑一件做爆袍,共有多少種組合?(求f(q))

遞歸算法:

 def fibonacci(n):
     if n==1 or n==2:
         return 1
     else:
         return fibonacci(n-1)+fibonacci(n-2)

算法分析

只要函數(shù)未到達(dá)遞歸出口(n<=2),就會不斷的在現(xiàn)有函數(shù)棧上開辟新的空間(形參表作郭、靜態(tài)鏈陨囊、動態(tài)鏈、返回地址)夹攒。所以當(dāng)遞歸太深的時候會出現(xiàn)棧溢出(stack overflow)蜘醋。
可以看出n>2后,每算一個數(shù)咏尝,都要遞歸調(diào)用前兩個數(shù)的和压语,而前兩個也要繼續(xù)向前遞歸,每次遞歸相當(dāng)于是重新在原有的函數(shù)棧幀上再次開辟空間來運(yùn)行函數(shù).

遞歸二叉樹

二叉樹的高度是 n - 1状土,高度為k的二叉樹最多可以由 2^k - 1個葉子節(jié)點(diǎn)无蜂,即調(diào)用2^n - 1次,所以時間復(fù)雜度為 O(2^n)蒙谓,而空間復(fù)雜度就是樹的高度 O(n)
算法指數(shù)級的時間復(fù)雜度斥季,隨著參數(shù)的增長很容易出現(xiàn)棧溢出。

一般我們認(rèn)為常數(shù)累驮、線性酣倾、多項(xiàng)式級別的復(fù)雜度可以忍受,而指數(shù)級的復(fù)雜度隨著規(guī)模增大谤专,計(jì)算效率急劇下降躁锡,無法應(yīng)用到實(shí)際問題中。相應(yīng)地置侍,不存在多項(xiàng)式復(fù)雜度算法的問題映之,被稱作難解的(intractable)問題。

非遞歸算法

每算一個數(shù)都要把前面的所有數(shù)重算一次蜡坊,其實(shí)從頭開始算杠输,保存下末尾的兩個數(shù)就好了,把遞歸變成迭代
優(yōu)化的方法很簡單秕衙,從頭算就行了蠢甲,每次算出的數(shù)存到隊(duì)列尾部,下一個要計(jì)算的數(shù)等于末尾兩個的和嘛据忘,這樣算過的就不用再算了鹦牛。搞糕。÷罚可以直接取到窍仰。。礼殊。

def fib(n):
    a=time.time()
    for i in range(n):
        if i==0:
            stack.append(1)
        elif i==1:
            stack.append(1)
        else:
            stack.append(stack[-1]+stack[-2])
    a=time.time()-a
    print('非遞歸結(jié)果??:'+str(stack[-1])+' 用時:'+str(a)+'s\n')

時間復(fù)雜度O(n),空間復(fù)雜度O(n)
下面是對比

n 遞歸算法O(2^n)(計(jì)算次數(shù)/時間) 非遞歸算法O(n)(計(jì)算次數(shù)/時間)
10 計(jì)算109次 用時:5.60e-05s 計(jì)算10次 用時:9.79e-05s
20 計(jì)算13529次 用時:0.0049s 計(jì)算20次 用時:4.31e-05s
30 計(jì)算1664079次 用時:0.27s 計(jì)算30次 用時:9.51e-05s
40 計(jì)算204668309次 用時:32s 計(jì)算40次 用時:5.19e-05s

遞歸算法n=50就要等待n=40的2^{10}倍辈赋,也就是512分鐘,9個小時膏燕,而非遞歸只需要1e5級別的時間,萬分之一秒內(nèi)完成悟民。算法何必為難算法坝辫。。射亏。

漢諾塔也屬于這類問題

例題 HDOJ2041 2044 2045 2046

HDOJ 2041

問題鏈接2041
Problem Description

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 87980    Accepted Submission(s): 45195

有一樓梯共M級近忙,剛開始時你在第一級,若每次只能跨上一級或二級智润,要走上第M級及舍,共有多少種走法?

Input

輸入數(shù)據(jù)首先包含一個整數(shù)N窟绷,表示測試實(shí)例的個數(shù)锯玛,然后是N行數(shù)據(jù),每行包含一個整數(shù)M(1<=M<=40),表示樓梯的級數(shù)兼蜈。

Output

對于每個測試實(shí)例攘残,請輸出不同走法的數(shù)量

Sample Input
2
2
3

Sample Output
1
2

問題分析:每次只能向上跨一級或者兩級,那么到M級只有兩種方法为狸,從M-1級跨一級或者從M-2級跨兩級歼郭。那么跨一級次數(shù)加一,跨兩級次數(shù)也是加一辐棒。把樹畫出來就發(fā)現(xiàn)是個遞歸了病曾。
f(1)=1 跨一步算一次\\ f(2)=1跨兩步算一次\\ f(m)=f(m-1)+f(m-2)
就是一個fibonacci數(shù)列。漾根。泰涂。
要注意三個問題

  • 1.類型范圍 這一題int就夠了,如果級數(shù)增加可能就需要long 等等了
  • 2.復(fù)雜度 上面我們分析過立叛,遞歸必然超時负敏,需要改成迭代或者棧方法
  • 3.輸出記得換行

ACcode

#include <iostream>
#include <stack>
#include <string>
using namespace std;
int fib(int i){
int sum=0,start=0,end=0;
for (int n=1;n<=i;n++){
    if (n==1){
        end=1;
    }
    else if(n==2){
        start=1;
    }
    else {
        sum=start+end;
        start=end;
        end=sum;
    }
}
return end;
}
int main(){
int num=0,Destination=0;
scanf("%d",&num);
for (int i=0;i<num;i++){
    scanf("%d",&Destination);
    printf("%d\n",fib(Destination));
}
return 0;
}

hdoj2044

hdoj2044問題鏈接

問題描述


image.png

問題分析:小蜜蜂在格子每次只有兩個選擇:1.直接向右走一步,序號加二 2.斜著走一步序號加一

f(1)=1 直接走算一次\\f(2)=1斜著走算一次\\f(M)=f(M-1)+f(M-2)
注意問題:

  • 1 從a到b相當(dāng)于從1到a-b+1秘蛇。
  • 2 b大于40其做,2^{40} int會越界顶考,要用long long

ACcode

#include <iostream>
long long go(long long n){
long long  step1=0,step2=0,sum=0;
    for (long long  i=1;i<=n;i++){
        if(i==1){
            step2=1;
        }
        else if (n==2){
            step1=1;
            step2=1;
        }
        else{
            sum=step1+step2;
            step1=step2;
            step2=sum;
            }
    }
    return step2;
}
int main(){
int num=0;
long long  start=0,end=0;
scanf("%d",&num);
for (int i=0;i<num;i++){
    scanf("%lld %lld",&start,&end);
    printf("%lld\n",go(end-start+1));
}
return 0;
}

hdoj2045 RPG難題

hdoj2045地址http://acm.hdu.edu.cn/showproblem.php?pid=2045

image.png

問題分析:問題重點(diǎn)在于正確推出遞推公式,推出來就和就是簡單的遞推了妖泄。

f(1)=3\\f(2)=6\\f(3)=6\\接下來分析f(4)的時候就發(fā)現(xiàn)驹沿,f(4)受前一個的結(jié)果影響。\\如果第n-1個格子與第1個格子顏色相同蹈胡,那么第n個格子可以取兩個顏色渊季,此時有2*f(n-2)種\\(為什么不是2*f(n-1)?)因?yàn)楦褡觧-1此時只能選和第1個一樣的一個\\而如果第n-1個格子和第1個格子顏色不同罚渐,第n個格子將會只有1種選擇此時有f(n)種\\ 所以n>3時有 f(n)=2*f(n-2)+f(n-1)
ACcode

#include <iostream>
#include <cstdio>
using namespace std;
long long fib(long long n){
        long long temp=0,step1=0,step2=0;
        for (long long i =1;i<=n;i++){
                if (i==1){
                        step2=3;
                }
                else if(i==2){
                        step1=3;
                        step2=6;
                }
                else if(i==3){
                        step1=6;
                        step2=6;
                }
                else{
                        temp=step2;
                        step2=2*step1+step2;
                        step1=temp;
                }
        }
        return step2;
        }

int main(){
long long num=0;
while(scanf("%lld",&num)!=EOF){
        printf("%lld\n",fib(num));
}
return 0;
}

hdoj 2046

骨牌鋪方格http://acm.hdu.edu.cn/showproblem.php?pid=2046


問題分析:看起來問題變成二維的了却汉,其實(shí)不然。骨牌只有兩個并列橫著放和單個豎著放兩種情況荷并。并列橫著放方格長度+2合砂,豎著放長度+1.問題就變成了每次可以走一米或者走兩米,問走n米有多少種走法源织。也是個fibonacci問題翩伪。

f(1)=1一塊骨牌豎著擺\\f(2)=2 兩塊骨牌橫著放\\f(m)=f(m-1)+f(m+1)
問題就變得簡單起來了。
ACcode

#include<iostream>
#include<cstdio>
long long queue[51]={1,2};
long long go(long long n){
    if (n==1)
        return 1;
    if (n==2)
        return 2;
for (int i=2;i<n;i++){
    queue[i]=queue[i-1]+queue[i-2];
}
return queue[n-1];
}
int main(){
long long n=0;
while(~scanf("%lld",&n)){
printf("%lld\n",go(n));
}
return 0;
}

hdoj2047 EOF牛肉串

http://acm.hdu.edu.cn/showproblem.php?pid=2047


問題分析:每次可以從E谈息、O缘屹、F三個字符串種選一個刻到牛肉串上,長度加1侠仇,兩個o不能連續(xù)轻姿。問刻n個字符有多少種刻法?

首先f(1)=3\\f(2)=8\\如果第n個字符為o逻炊,那么第n-1就只能取E踢代、F兩種,有2*f(n-2)種\\如果第n個字符串不為o嗅骄,可以取E胳挎、F,則第n-1個字符就沒有被n限制溺森,有2*f(n-1)種\\所以f(n)=f(n-1)+f(n-2)
ACcode

#include <iostream>
#include <cstdio>
long long chuan[41]={3,8};
long long cow(long long n){
    if (n==1)
        return 3;
    if (n==2)
        return 8;
    for (int i=2;i<n;i++){
    chuan[i]=2*chuan[i-2]+2*chuan[i-1];
    }
return chuan[n-1];
}
int main(){
long long n=0;
while(~scanf("%lld",&n)){
    printf("%lld\n",cow(n));
}
return 0;
}

fibonacci 測速小腳本

import time
stack=[]
#非遞歸寫法:(棧)
numre=0
numinre=0
def fib(n):
    global numinre
    a=time.time()
    for i in range(n):
        if i==0:
            stack.append(1)
        elif i==1:
            stack.append(1)
        else:
            stack.append(stack[-1]+stack[-2])
        numinre+=1
    a=time.time()-a
    print('非遞歸結(jié)果??:'+str(stack[-1])+' 計(jì)算'+str(numinre)+'次'+' 用時:'+str(a)+'s\n')
#遞歸寫法
def fibonacci(n):
    global numre
    numre+=1
    if n==1 or n==2:
        return 1
    else:
        return fibonacci(n-1)+fibonacci(n-2)
if __name__=='__main__':
    print('????fibonacci遞歸與非遞歸算法速度對比\n')
    x=int(input('??請輸入n:'))
    fib(x)
    t=time.time()
    re=fibonacci(x)
    t=time.time()-t
    print('??遞歸結(jié)果??:'+str(re)+' 計(jì)算'+str(numre)+'次'+' 用時:'+str(t)+'s\n')
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慕爬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子屏积,更是在濱河造成了極大的恐慌医窿,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炊林,死亡現(xiàn)場離奇詭異姥卢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門独榴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僧叉,“玉大人,你說我怎么就攤上這事棺榔∑慷椋” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵症歇,是天一觀的道長郎笆。 經(jīng)常有香客問我,道長忘晤,這世上最難降的妖魔是什么宛蚓? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮设塔,結(jié)果婚禮上苍息,老公的妹妹穿的比我還像新娘。我一直安慰自己壹置,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布表谊。 她就那樣靜靜地躺著钞护,像睡著了一般。 火紅的嫁衣襯著肌膚如雪爆办。 梳的紋絲不亂的頭發(fā)上难咕,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音距辆,去河邊找鬼余佃。 笑死,一個胖子當(dāng)著我的面吹牛跨算,可吹牛的內(nèi)容都是我干的爆土。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼诸蚕,長吁一口氣:“原來是場噩夢啊……” “哼步势!你這毒婦竟也來了家浇?” 一聲冷哼從身側(cè)響起瓤的,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎润脸,沒想到半個月后漠魏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倔矾,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哪自。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丰包。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖提陶,靈堂內(nèi)的尸體忽然破棺而出烫沙,到底是詐尸還是另有隱情,我是刑警寧澤隙笆,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布锌蓄,位于F島的核電站,受9級特大地震影響撑柔,放射性物質(zhì)發(fā)生泄漏瘸爽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一铅忿、第九天 我趴在偏房一處隱蔽的房頂上張望剪决。 院中可真熱鬧,春花似錦檀训、人聲如沸柑潦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渗鬼。三九已至,卻和暖如春荧琼,著一層夾襖步出監(jiān)牢的瞬間譬胎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工命锄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堰乔,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓脐恩,卻偏偏與公主長得像镐侯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子驶冒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

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