国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

遞歸算法的非遞歸化剖析

2021-07-19 23:49:22陳韶鈺孫娟
電腦知識(shí)與技術(shù) 2021年13期
關(guān)鍵詞:迭代

陳韶鈺 孫娟

摘要:在數(shù)據(jù)結(jié)構(gòu)的教學(xué)中,我們經(jīng)常用到遞歸,例如廣義表,二叉樹等,但是在課本中講到遞歸算法的非遞歸化卻寥寥數(shù)語,并且很多學(xué)生也問到這個(gè)問題。該文針對這一情況研究遞歸函數(shù)的非遞歸化。該文根據(jù)是否是尾遞歸進(jìn)行分類,重點(diǎn)講解兩種不同的非遞歸化方法,其中一種轉(zhuǎn)換成循環(huán)來實(shí)現(xiàn)非遞歸化,但是對于復(fù)雜的非尾遞歸則使用棧來模擬系統(tǒng)棧的工作方式來實(shí)現(xiàn)非遞歸化,最后給出遞歸和非遞歸化的比較,根據(jù)問題的實(shí)際情況選擇是否采用遞歸。

關(guān)鍵詞:遞歸算法;非遞歸化;尾遞歸;迭代;非尾遞歸;棧

中圖分類號:TP3? ? ? ? 文獻(xiàn)標(biāo)識(shí)碼:A

文章編號:1009-3044(2021)13-0202-03

1 遞歸算法的概述

什么是遞歸?有這樣一個(gè)非常典型的例子:從前有座山,山上有座廟,廟里有個(gè)老和尚,老和尚在給小和尚講故事,故事講的是從前有座山,山上有座廟,廟里有個(gè)老和尚,老和尚在給小和尚講故事,故事講的是......遞歸函數(shù)就是一個(gè)直接調(diào)用自己或通過一系列的調(diào)用語句間接調(diào)用自己的函數(shù)。遞歸函數(shù)包含三種:第一,有很多遞歸定義的數(shù)學(xué)函數(shù),例如階乘,斐波那契數(shù)列;第二,有本身具有遞歸特性的數(shù)據(jù)結(jié)構(gòu),例如二叉樹,廣義表等;第三種有些問題遞歸求解比迭代求解更加簡單,例如Hanoi塔問題,八皇后問題等。

此外,遞歸有三個(gè)要素:第一,遞歸邊界條件:確定遞歸到何時(shí)終止,即遞歸出口;第二,遞歸模式:大問題如何分解為小問題的,即遞歸體;第三,遞歸的調(diào)用次數(shù)必須是有限的,每次遞歸調(diào)用后必須越來越接近某種限制條件。實(shí)際上,遞歸就是把不好解決的復(fù)雜問題分解為一個(gè)或多個(gè)小問題,再把這些小問題分解成更小的問題,直到每個(gè)小問題都可以直接解決。

在執(zhí)行遞歸算法時(shí),它包括遞推和回歸兩個(gè)過程。在進(jìn)行遞推過程中,就是將大問題分解為若干小問題,再進(jìn)行求解,且必須要有終止遞歸的條件;在進(jìn)行回歸時(shí),得到小問題的解,并且依次返回,求得較大問題的解,最后取得最大問題的解。

2 遞歸算法的特點(diǎn)

遞歸使得程序簡潔明了,理解起來比較輕松,但是遞歸算法中來回切換函數(shù)調(diào)用現(xiàn)場浪費(fèi)時(shí)間,并且系統(tǒng)提供棧來保存執(zhí)行現(xiàn)場需要大量的空間。首先,系統(tǒng)設(shè)立一個(gè)遞歸工作棧來保證遞歸函數(shù)的正確執(zhí)行。該系統(tǒng)工作棧是遞歸函數(shù)運(yùn)行期間使用的數(shù)據(jù)存儲(chǔ)區(qū)。當(dāng)調(diào)用一個(gè)遞歸函數(shù)時(shí),系統(tǒng)會(huì)自動(dòng)構(gòu)建一個(gè)工作記錄,稱為棧幀,棧幀放在棧的最上面。開始的時(shí)候,棧幀僅僅存放返回地址和指向上一個(gè)幀的指針。每進(jìn)入一層遞歸,就把新的記錄壓入棧頂。每退出一層遞歸,就從棧頂彈出一個(gè)工作記錄(即把棧幀刪除),直到程序控制返回原調(diào)用函數(shù)。于是,遞歸實(shí)現(xiàn)的空間效率并不高,又因?yàn)轭l繁的壓棧和出棧時(shí)間開銷也非常大??傊f歸存在一個(gè)致命的缺點(diǎn)就是,遞歸的深度太深會(huì)導(dǎo)致堆棧溢出。

3 遞歸算法轉(zhuǎn)換為非遞歸算法的典型案例

在教學(xué)過程中,很多同學(xué)都以為使用棧將遞歸算法轉(zhuǎn)換為非遞歸算法,其實(shí)這種想法是錯(cuò)誤的。遞歸算法分為尾遞歸和非尾遞歸。尾遞歸直接用循環(huán)來實(shí)現(xiàn)遞歸不需要棧來輔助,非尾遞歸則要使用棧來實(shí)現(xiàn)非遞歸算法。

(1)尾遞歸轉(zhuǎn)換成非遞歸算法

尾遞歸就是當(dāng)遞歸調(diào)用時(shí)最后執(zhí)行的語句是遞歸語句并且函數(shù)體中包含遞歸體的返回值不是表達(dá)式的一部分。在回歸的過程中,尾遞歸函數(shù)不需要任何操作?,F(xiàn)在很多的編譯器利用這一特點(diǎn),使得代碼得到優(yōu)化。

尾遞歸的工作原理:當(dāng)編譯器檢測到是尾遞歸函數(shù)調(diào)用時(shí),每一層遞歸信息構(gòu)成的工作記錄就覆蓋棧頂記錄,不會(huì)增加遞歸的深度。于是,每進(jìn)入一層遞歸,棧頂存放的就是最后一條準(zhǔn)備執(zhí)行的記錄,這也就是說尾遞歸的棧的深度為1.但是這些并不是說明尾遞歸不需要棧,只是棧幀沒有其他的事情可以做,所以可以用循環(huán)來實(shí)現(xiàn)非遞歸化。也就是說,每一次遞歸調(diào)用只需要覆蓋棧幀,大大縮減了棧空間,所以非遞歸化時(shí)不需要手動(dòng)的壓棧和出棧,大大提高了運(yùn)行的效率。

例1:求最大公約數(shù)

算法1:利用尾遞歸求最大公約數(shù)

int gcd1(int big,int small)

{

if(big%small==0)

return? small;

else

Return gcd(small,big%small);

}

算法2:利用循環(huán)求最大公約數(shù)

int gcd2(int a,int b)

{

while(b)

{

int t=a%b;

a=b;

b=t;

}

return a;

}

例2:求解n!

算法1:使用非尾遞歸實(shí)現(xiàn)n!

int jiecheng3(int n)

{

if(n==0||n==1)

return 1;

else

return n*jiecheng3(n-1);

}

算法2:利用尾遞歸實(shí)現(xiàn)n!

int jiecheng(int n,int a)

{

return (n==1)?a:jiecheng(n-1,n*a);

}

算法3:利用循環(huán)實(shí)現(xiàn)非遞歸n!

int jiecheng2(int n)

{

int result=1;

if(n<0)

return 0;

else if(n==0||n==1)

return 1;

int i=1;

While(n>=i)

{

result=result*i;

i++;

}

}

從上面兩個(gè)例子可以看出,實(shí)現(xiàn)尾遞歸往往需要改寫遞歸函數(shù),確保最后一步調(diào)用自身。要把普通的遞歸函數(shù)轉(zhuǎn)換為尾遞歸函數(shù)就須把所有用到的內(nèi)部變量改寫成函數(shù)的參數(shù)??傊彩悄芨膶懗晌策f歸的函數(shù)在非遞歸化時(shí),都能用循環(huán)實(shí)現(xiàn)。雖然有些函數(shù)可以改寫成尾遞歸,但是在一定程度上降低了程序的可讀性。

(2)非尾遞歸函數(shù)借助棧來實(shí)現(xiàn)非遞歸化

顧名思義,非尾遞歸是遞歸執(zhí)行不是最后一句或?qū)儆诒磉_(dá)式的一部分。對于非尾遞歸而言,遞歸的過程是編譯器處理了壓棧和出棧的操作,轉(zhuǎn)換為迭代函數(shù)就需要手動(dòng)地處理壓棧和出棧。

例3:n階漢諾塔問題

算法1:利用遞歸算法實(shí)現(xiàn)漢諾塔問題

Void move(char from,int n,char to)

{

println(n+”號”+”from”+from+”to”+to);

}

void hanoi(int n,char A,char B,char C)

{

if(n==1)

move(A,1,C);//將編號為1的圓盤從A移到C

else{

hanoi(n-1,A,C,B);//將編號為n-1的圓盤,借助C盤從A移到B

move(A,n,C);//將編號n的圓盤從A移到C

hanoi(n-1,B,A,C);//將編號為n-1的圓盤,借助A盤從B移到A

}

}

算法2:利用非遞歸算法實(shí)現(xiàn)漢諾塔問題

struct act{

int num;

char x,y,z;

}s[max];

void hanoi(int n,char a,char b,char c)

{

int top=-1,count=0;

While()

{

if(n>0){//將編號為n-1的圓盤,借助C盤從A移到B

s[++top].num=n--;

s[top].x=a;

s[top].y=b;

s[top].z=c;

a=s[top].x;

b=s[top].y;

c=s[top].z;

}

else{

println(“%d:%d%c->%c”,++count,s[top].num,s[top].x,s[top].z);//將編號n的圓盤從A移到C

n=s[top].num-1;//將編號為n-1的圓盤,借助A盤從B移到A

a=s[top].y;

b=s[top].x;

c=s[top].z;

top--;

}

}

}

例4:中序遍歷二叉樹

算法1:使用遞歸算法中序遍歷二叉樹

void inordertraverse(BiTree t)

{

if(t)

{

inordertraverse( t->leftchild);//遍歷左子樹

visit(t->data);

inordertraverse( t->rightchild);//遍歷右子樹

}

}

算法2:使用非遞歸算法中序遍歷二叉樹

void inordertraverse(BiTree? t)

{

Initstack(s);

BiTree p=t;

while(p||!StackEmpty(s)){

if(p){//根指針進(jìn)棧,遍歷左子樹

Push(s,p);

p=p->leftchild;

}

else//根指針退棧,遍歷右子樹

{

Pop(s,p);

visit(p->data);

p=p->rightchild;

}

}

return;

}

4 遞歸算法與非遞歸算法的比較

由上述例子可以發(fā)現(xiàn),遞歸方式設(shè)計(jì)的算法實(shí)現(xiàn)簡潔,思路清晰,具有良好的可讀性和可維護(hù)性,很容易證明該算法的正確性。但是,遞歸程序的執(zhí)行效率一般低于非遞歸程序。尤其是遞歸深度較深時(shí),就造成空間耗費(fèi)大,這是遞歸算法最致命的弱點(diǎn)。

在要求執(zhí)行效率比較高的情況下,我們一般選用非遞歸算法。根據(jù)尾遞歸的工作原理,遞歸所使用的??臻g就很大程度的縮減了,運(yùn)行當(dāng)中雖然利用到了堆棧,但是棧的深度為1。這樣在轉(zhuǎn)化為非遞歸化函數(shù)時(shí),利用迭代就可以實(shí)現(xiàn)非遞歸化,這樣運(yùn)行效率會(huì)變得很高。非尾遞歸利用棧來實(shí)現(xiàn)遞歸的非遞歸化,手動(dòng)的壓棧和出棧,難于編寫,出錯(cuò)率高。理論上,遞歸算法一般可轉(zhuǎn)化為非遞歸算法,但是有一些算法很難實(shí)現(xiàn)非遞歸化,所以要根據(jù)實(shí)際情況選擇是遞歸還是非遞歸。

5 結(jié)束語

在數(shù)據(jù)結(jié)構(gòu)的教學(xué)中,遞歸算法的非遞歸化一直是教學(xué)中的重點(diǎn)和難點(diǎn)。在遇到一個(gè)問題時(shí),要根據(jù)具體情況具體分析看是否使用遞歸算法。本文首先分析遞歸的特點(diǎn),然后分析遞歸轉(zhuǎn)化為非遞歸的兩種方式,使學(xué)生很容易掌握遞歸函數(shù)的非遞歸化。

參考文獻(xiàn):

[1] 李瑩,孫承福.數(shù)據(jù)結(jié)構(gòu)[M].北京:清華大學(xué)出版社,2013.

[2] 高漢平,方志雄.從遞歸算法到非遞歸的變換[J].黃岡師范學(xué)院學(xué)報(bào),2002,22(3):47-50.

[3] 施伯樂,等.數(shù)據(jù)結(jié)構(gòu)[M].上海:復(fù)旦大學(xué)出版社,1988.

[4] 孟林,尹德輝.遞歸算法本質(zhì)及非遞化的一般規(guī)律[J].福建電腦,2004,20(1):12-46.

[5] 高大鵬.C語言教學(xué)中的語言技巧[J].科技信息,2012(27):217,525.

[6] 周法國,韓智,高天.遞歸算法設(shè)計(jì)思想與策略分析[J].軟件導(dǎo)刊,2017,16(10):35-38.

[7] 李云鶴,武善玉,鐘鳴.最優(yōu)二叉樹編譯碼確定的一種新方法[J].茂名學(xué)院學(xué)報(bào),2003,13(4):42-44,64.

[8] 高鷺,周李涌.遞歸算法及其轉(zhuǎn)化為非遞歸算法的分析[J].科技資訊,2008,6(30):210.

[9] 姚俊明,邢丹,厲群,等.數(shù)據(jù)結(jié)構(gòu)課程中遞歸教學(xué)的深入探討[J].電腦知識(shí)與技術(shù),2011,7(14):3382-3383,3385.

【通聯(lián)編輯:代影】

猜你喜歡
迭代
斐波那契數(shù)列研究及編程實(shí)現(xiàn)
RANSAC算法求解單應(yīng)矩陣的具體研究
基于省級精品教材多元自主學(xué)習(xí)平臺(tái)的螺旋上升學(xué)習(xí)研究
基于最小二乘的視野區(qū)域運(yùn)動(dòng)方向分析
JavaScript計(jì)算性能對比研究
中間件“迭代”
一種用于室內(nèi)定位的線性規(guī)劃算法
DNS解析的探究
考試周刊(2016年64期)2016-09-22 18:18:03
漲價(jià)與醫(yī)保政策需同步“迭代”
一種快速有效的相位檢索算法
卫辉市| 吉林市| 隆回县| 玉龙| 桃江县| 泰和县| 正阳县| 金溪县| 西乌珠穆沁旗| 新河县| 崇义县| 古丈县| 罗定市| 绿春县| 太仓市| 揭阳市| 蒙山县| 中牟县| 昌乐县| 嵊州市| 梨树县| 和政县| 桐柏县| 双江| 天峨县| 江城| 廊坊市| 贺州市| 阿合奇县| 盐边县| 芜湖市| 台安县| 龙海市| 天水市| 乌拉特中旗| 台北县| 开化县| 通化市| 基隆市| 梅州市| 富锦市|