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

?

基于分治策略實(shí)現(xiàn)模式化構(gòu)建遞歸函數(shù)

2020-03-30 03:19楊勇
電腦知識(shí)與技術(shù) 2020年1期

楊勇

摘要:遞歸算法在程序設(shè)計(jì)的許多領(lǐng)域都有重要應(yīng)用,為了寫(xiě)出思路清晰,邏輯嚴(yán)謹(jǐn)?shù)倪f歸函數(shù),以分治策略為基礎(chǔ),歸納出組成遞歸函數(shù)的三大部分:提前終止條件,子問(wèn)題分解,結(jié)果合并。并探討了子問(wèn)題分解的幾種模式,研究了各種模式下遞歸函數(shù)的設(shè)計(jì)思路,為模式化構(gòu)建遞歸函數(shù)找到可行途徑。

關(guān)鍵詞:遞歸;分治策略;子問(wèn)題分解;回溯;棧區(qū)

中圖分類(lèi)號(hào):TP311.11 文獻(xiàn)標(biāo)識(shí)碼:A

文章編號(hào):1009-3044(2020)01-0264-03

1背景

c語(yǔ)言程序由函數(shù)組成,通過(guò)函數(shù)之間的調(diào)用完成程序功能。如果函數(shù)在其執(zhí)行語(yǔ)句中又調(diào)用自己,形成函數(shù)的遞歸調(diào)用。遞歸在數(shù)據(jù)結(jié)構(gòu)和算法設(shè)計(jì)領(lǐng)域應(yīng)用非常廣泛,但函數(shù)遞歸調(diào)用一直是c語(yǔ)言學(xué)習(xí)和研究中的一個(gè)難點(diǎn)。對(duì)遞歸中函數(shù)調(diào)用順序,函數(shù)返回值,遞歸終止條件有許多模糊的,錯(cuò)誤的認(rèn)識(shí),不能正確地推理遞歸結(jié)果。對(duì)運(yùn)用遞歸方法寫(xiě)出思路清晰,代碼簡(jiǎn)潔的遞歸函數(shù)則更視為畏途。本文分析了遞歸過(guò)程系統(tǒng)底層運(yùn)行機(jī)理,對(duì)編寫(xiě)遞歸函數(shù)的思路做了探討和分析。

2遞歸調(diào)用系統(tǒng)底層運(yùn)行機(jī)理

遞歸調(diào)用是一種特殊的函數(shù)嵌套調(diào)用。當(dāng)一個(gè)函數(shù)中某個(gè)語(yǔ)句開(kāi)始調(diào)用另外一個(gè)函數(shù)時(shí),在操作系統(tǒng)底層上意味著當(dāng)前正在運(yùn)行的一段執(zhí)行指令序列中途暫停,不往下執(zhí)行,轉(zhuǎn)而去執(zhí)行另一段指令序列。執(zhí)行完畢后,又回到原來(lái)執(zhí)行序列的暫停位置處,從暫停位置開(kāi)始繼續(xù)往下執(zhí)行。每一個(gè)函數(shù)被調(diào)用時(shí),系統(tǒng)都將為其分配棧區(qū)空間,把函數(shù)形參和局部變量放在棧區(qū)空間里。函數(shù)運(yùn)行結(jié)束,則系統(tǒng)回收為該函數(shù)分配的棧區(qū)內(nèi)存,棧頂指針下移。一個(gè)函數(shù)調(diào)用自己,實(shí)質(zhì)上與函數(shù)去調(diào)用其他函數(shù)沒(méi)有任何區(qū)別,也將為之分配獨(dú)立的棧區(qū)空間,放置各自的形參和局部變量。例如F(int a)函數(shù):

函數(shù)執(zhí)行到語(yǔ)句F(a-1);時(shí),以參數(shù)a-1調(diào)用自己,系統(tǒng)為F(a01)在棧區(qū)空間分配內(nèi)存,存儲(chǔ)F(a-1)函數(shù)的形參和局部變量。F(a-1)函數(shù)執(zhí)行時(shí)會(huì)再次調(diào)用自己,形成循環(huán),這個(gè)循環(huán)是不能結(jié)束的,系統(tǒng)也不斷在棧區(qū)為新調(diào)用的F函數(shù)分配內(nèi)存。直到棧區(qū)空間耗盡。因此必須有一個(gè)終止循環(huán)調(diào)用的機(jī)制,我們?cè)诤瘮?shù)調(diào)用自己的語(yǔ)句之前,設(shè)定某種條件,當(dāng)條件滿(mǎn)足時(shí),函數(shù)提前結(jié)束。本函數(shù)結(jié)束了,前面處于等待狀態(tài)的函數(shù)按調(diào)用的逆序逐個(gè)結(jié)束,整個(gè)遞歸也就可以終止。

以求斐波那契數(shù)列為例,解析遞歸函數(shù)調(diào)用過(guò)程。參數(shù)n為斐波那契數(shù)列的項(xiàng)數(shù),以0開(kāi)始,設(shè)n=6,每次調(diào)用以方框表示,畫(huà)出調(diào)用圖示:

圖1中實(shí)線(xiàn)箭頭表示調(diào)用方向,數(shù)字表示調(diào)用順序,虛線(xiàn)表示函數(shù)返回位置。調(diào)用過(guò)程形成二叉樹(shù)的形式,但實(shí)際運(yùn)行時(shí),這些分支不會(huì)同時(shí)運(yùn)行,調(diào)用中始終只存在單鏈。fibo(6沖進(jìn)入遞歸時(shí)的語(yǔ)句為fibo(5)+fibo(4),實(shí)際上,第二個(gè)函數(shù)調(diào)用fibo(4)并沒(méi)有開(kāi)始,要等到fibo(5)調(diào)用返回后,才會(huì)進(jìn)2k.fibo(4)。依次類(lèi)推,進(jìn)入fibo(5)后先調(diào)用fibo(4),進(jìn)2k.fibo(4)后先調(diào)用fibo(3),由于fibo(3)滿(mǎn)足提前返回的條件,開(kāi)始返回,不再遞歸,返回2,回到fibo(4)中,fibo(4)再開(kāi)始調(diào)用fibo(2),不遞歸,得到結(jié)果1,與fibo(3)的返回結(jié)果相加得3,fibo(4)調(diào)用結(jié)束返回3回到矗一bo(51中,后面調(diào)用過(guò)程可作類(lèi)似分析,不再贅述。

分析遞歸過(guò)程的例子,總結(jié)出如下一些特征:

1)函數(shù)遞歸調(diào)用時(shí),形參在不斷變化,一般來(lái)說(shuō),形參的值向縮小的放向變化,直到觸發(fā)提前結(jié)束函數(shù)的條件,當(dāng)前運(yùn)行函數(shù)不再遞歸,函數(shù)按調(diào)用的逆序依次結(jié)束。

2)每個(gè)被調(diào)用的遞歸函數(shù)都擁有獨(dú)立的棧區(qū)空間,各函數(shù)的形式參數(shù)和局部變量處于不同的棧區(qū)空間里,相互獨(dú)立。

3)整個(gè)遞歸調(diào)用可以有多個(gè)分支,但分支不會(huì)同時(shí)運(yùn)行,調(diào)用鏈?zhǔn)冀K為單鏈,只有鏈條末端的函數(shù)在運(yùn)行中,而其他函數(shù)則處于暫停狀態(tài)。

3運(yùn)用分治策略構(gòu)建遞歸函數(shù)

遞歸函數(shù)解決問(wèn)題的過(guò)程,實(shí)際上是算法中分治策略的體現(xiàn)。所謂分治,是把一個(gè)復(fù)雜問(wèn)題分解為若干子問(wèn)題,若子問(wèn)題有簡(jiǎn)單的方法得出解,整個(gè)問(wèn)題的解則是所有子問(wèn)題解答的合并。如果分解出的子問(wèn)題仍然不易解決,則繼續(xù)對(duì)子問(wèn)題分解,直到分解為能解決的子問(wèn)題為止,函數(shù)遞歸的過(guò)程就是對(duì)復(fù)雜子問(wèn)題分解的過(guò)程?;厮菟惴ㄒ步?jīng)常使用遞歸來(lái)實(shí)現(xiàn)?;厮菟惴ㄖ械淖訂?wèn)題之間,是相互關(guān)聯(lián)的,下一個(gè)子問(wèn)題的解依賴(lài)于上一個(gè)子問(wèn)題的解。分治算法中的子問(wèn)題也有一定的關(guān)聯(lián)性。只是沒(méi)有回溯算法中那么緊密。從大的角度來(lái)看,回溯算法也屬于分治策略。

以分治策略為基礎(chǔ),為使求解過(guò)程遵循較為固定的步驟,將遞歸函數(shù)大體分為三部分:

第一部分:寫(xiě)出提前終止,不再遞歸的條件。當(dāng)遞歸函數(shù)不斷調(diào)用,問(wèn)題不斷分解,規(guī)模不斷縮小,縮小到一定邊界之內(nèi),符合一定條件時(shí),此時(shí)的子問(wèn)題是一個(gè)最簡(jiǎn)子問(wèn)題,可以直接解決,無(wú)須進(jìn)一步遞歸。條件設(shè)定一般由函數(shù)形參的變化來(lái)完成。

第二部分:?jiǎn)栴}分解。分解成兩個(gè)或多個(gè)子問(wèn)題,簡(jiǎn)單子問(wèn)題直接解決,復(fù)雜子問(wèn)題不能直接解決,交給遞歸函數(shù)。解決子問(wèn)題的算法應(yīng)該與解決整個(gè)問(wèn)題的算法完全相同,區(qū)別只在于處理的規(guī)模不同,或處理時(shí)的參數(shù)不同。要注意的是,子問(wèn)題分解,有時(shí)需要先做一定的前處理工作,之后才能明晰的劃分子問(wèn)題。

第三部分:子問(wèn)題結(jié)果合并。簡(jiǎn)單子問(wèn)題的解很直觀,而復(fù)雜子問(wèn)題的解是什么?就是遞歸函數(shù)的返回值(如果沒(méi)有返回值,則是函數(shù)運(yùn)行中的所有輸出值),這是理解結(jié)果合并方法的要點(diǎn)。合并可以是子問(wèn)題的解經(jīng)過(guò)簡(jiǎn)單計(jì)算后合并。也可以是復(fù)雜處理運(yùn)算后再合并。

遞歸函數(shù)三個(gè)組成部分中,第二部分如何正確劃分子問(wèn)題,是設(shè)計(jì)遞歸函數(shù)的關(guān)鍵。把子問(wèn)題分解模式歸納為如下幾種,并按上述三大部分來(lái)構(gòu)建遞歸函數(shù)。

3.1問(wèn)題=1個(gè)簡(jiǎn)單子問(wèn)題+1個(gè)復(fù)雜子問(wèn)題

簡(jiǎn)單子問(wèn)題直接在函數(shù)中解決,復(fù)雜子問(wèn)題交由函數(shù)遞歸解決。這是遞歸函數(shù)最常見(jiàn)的問(wèn)題分解模式。例,求字符串“ABCD”的全排列:

1)問(wèn)題:輸出字符串全排列。

2)簡(jiǎn)單子問(wèn)題:字符串第一個(gè)位置作全排列(即字符串中各個(gè)字符輪流放在第1位1

3)復(fù)雜子問(wèn)題:除開(kāi)第一個(gè)字符,剩下的子字符串再做全排列,以圖示表示:

4)提前結(jié)束條件:剩下的子字符串只有一個(gè)字符了

5)結(jié)果合并:當(dāng)剩下的字符串只有一個(gè)字符時(shí),全排列完成,輸出整個(gè)字符串

通過(guò)循環(huán)把各個(gè)字符輪流交換到第一個(gè)位置,剩下的少了一個(gè)字符的字符串再做全排列,其算法同整個(gè)字符串全排列完全相同,交由函數(shù)遞歸完成。遞歸調(diào)用時(shí),傳給函數(shù)的字符串會(huì)越來(lái)越短,當(dāng)傳入函數(shù)的字符串只有一個(gè)字符時(shí),無(wú)須再排列,函數(shù)提前結(jié)束,不進(jìn)入遞歸。寫(xiě)出字符串全排列函數(shù)如下:

簡(jiǎn)單子問(wèn)題與復(fù)雜子問(wèn)題的結(jié)果合并時(shí),要注意合并的順序,有時(shí),復(fù)雜子問(wèn)題的結(jié)果要置于簡(jiǎn)單子問(wèn)題的結(jié)果之前。例如將整數(shù)轉(zhuǎn)換為字符串輸出,思路如下:

1)問(wèn)題:整數(shù)轉(zhuǎn)為字符串

2)簡(jiǎn)單子問(wèn)題:把整數(shù)的個(gè)位轉(zhuǎn)為字符。(求整數(shù)對(duì)10的余數(shù)可得個(gè)位數(shù))

3)復(fù)雜子問(wèn)題:整數(shù)去掉個(gè)位后的部分整數(shù)轉(zhuǎn)為字符串,遞歸調(diào)用求解。

4)提前結(jié)束條件:整數(shù)只有個(gè)位了,直接轉(zhuǎn)為字符。

5)結(jié)果合并:先輸出遞歸函數(shù)結(jié)果,再輸出個(gè)位轉(zhuǎn)換結(jié)果。

整數(shù)轉(zhuǎn)為字符串一定是前面的部分整數(shù)先輸出,再輸出最后個(gè)位字符,所以遞歸函數(shù)要放在個(gè)位字符輸出之前。再進(jìn)一步思考,如果改變合并順序個(gè)位字符先輸出,遞歸函數(shù)后輸出,那么結(jié)果將得到原整數(shù)的倒序字符串。

3.2問(wèn)題=1個(gè)復(fù)雜子問(wèn)題+1個(gè)復(fù)雜子問(wèn)題。

這種情況兩個(gè)復(fù)雜子問(wèn)題都由函數(shù)遞歸解決。然后將兩個(gè)子問(wèn)題的結(jié)果合并。合并時(shí)有簡(jiǎn)單合并,也有經(jīng)過(guò)復(fù)雜處理后再合并。例如,求二叉樹(shù)的高度,問(wèn)題分解得:

1)問(wèn)題:求根為root的二叉樹(shù)的高度

2)復(fù)雜子問(wèn)題一:求二叉樹(shù)根的左子樹(shù)高度,遞歸調(diào)用求解

3)復(fù)雜子問(wèn)題二:求二叉樹(shù)根的右子樹(shù)高度,遞歸調(diào)用求解

4)提前結(jié)束條件:所求樹(shù)為空樹(shù),根是NULL,高度為-1;

5)子問(wèn)題合并:左右兩個(gè)子樹(shù)高度的較大值+1為整個(gè)樹(shù)的高度

很多情況下,子問(wèn)題劃分工作并非簡(jiǎn)單明了,必須先做一定的處理,才能開(kāi)始劃分。例如快速排序法,對(duì)于要排序的數(shù)列,選取一個(gè)中間數(shù),將其余數(shù)置于中間數(shù)的兩邊,小數(shù)在左邊,大數(shù)在右邊,對(duì)以中間數(shù)為分界的兩個(gè)子序列分別排序,合并起來(lái)就是整個(gè)數(shù)列的排序。排序函數(shù)qnisort執(zhí)行過(guò)程是:

1)提前返回條件:子序列只有一個(gè)元素

2)子問(wèn)題劃分前處理:選取中間數(shù),將其余元素按左小右大的原則分別置于中間數(shù)的兩邊。

3)劃分子問(wèn)題:復(fù)雜子問(wèn)題一:對(duì)中間數(shù)左邊序列排序遞歸調(diào)用quicksort

4)劃分子問(wèn)題:復(fù)雜子問(wèn)題二:對(duì)中間數(shù)右邊序列排序遞歸調(diào)用quicksort

5)左右兩個(gè)排序子序列合并。

3.3復(fù)雜問(wèn)題=n個(gè)復(fù)雜子問(wèn)題之和

如果各復(fù)雜子問(wèn)題算法相同,但是各子問(wèn)題傳人參數(shù)與子問(wèn)題解決的先后順序有關(guān)。這時(shí)可以采取依照一定順序逐個(gè)解決子問(wèn)題的策略。例如著名的八皇后問(wèn)題:8x8的國(guó)際象棋盤(pán)上放上8個(gè)皇后,任意兩個(gè)皇后不能在同一行,列或?qū)蔷€(xiàn)上。問(wèn)有多少種擺放方法?分析:8個(gè)皇后只能處于不同列上,即一列上只能擺一個(gè)皇后,各列上皇后擺放的行不能相同,對(duì)角線(xiàn)也不能沖突,由此將問(wèn)題抽象為:在棋盤(pán)不同列上為每一個(gè)皇后找到符合條件的行位置,當(dāng)八個(gè)列上都擺上皇后時(shí),就得到問(wèn)題的一個(gè)解。

1)問(wèn)題:依列順序求各列皇后擺放的行

2)問(wèn)題分解:有8個(gè)子問(wèn)題,每個(gè)子問(wèn)題是:給某列上的皇后查找正確的行位置。

3)提前結(jié)束條件,8列皇后都找到位置?;蛘邤[到某一列皇后時(shí),已經(jīng)找不到合適的行。

4)子問(wèn)題合并,當(dāng)8列皇后都能找到位置時(shí),輸出結(jié)果。

建立一個(gè)數(shù)組queens[8],各元素的下標(biāo)代表皇后所處的列,各元素的值代表皇后所在的行,遞歸函數(shù)只針對(duì)子問(wèn)題求解既可,構(gòu)建函數(shù)如下:

其中valid_row函數(shù)判斷index列的皇后能否放在某一行上。在eight_queen函數(shù)中,對(duì)于index列的皇后,如果輪詢(xún)8個(gè)行位置都找不到符合條件的行位置時(shí),函數(shù)也將結(jié)束不產(chǎn)生遞歸,此時(shí)函數(shù)將返回到index-1列的函數(shù)循環(huán)中,繼續(xù)查找in-dex-1列皇后下一個(gè)合適的行位置,遞歸則在另一個(gè)分支上繼續(xù)進(jìn)行,所以,eight_queen函數(shù)有兩種終止遞歸的條件。

4結(jié)束語(yǔ)

本文探討了函數(shù)遞歸調(diào)用操作系統(tǒng)運(yùn)行機(jī)理,梳理出遞歸調(diào)用過(guò)程的幾個(gè)關(guān)鍵性特征,歸納出遞歸函數(shù)的三大組成部分,并探討了分治策略下問(wèn)題分解的幾種模式,對(duì)每一種模式下構(gòu)建遞歸函數(shù)的思路以具體例子加以分析。以本文總結(jié)的遞歸構(gòu)建模式,可以快速、準(zhǔn)確地寫(xiě)出思路清晰,邏輯嚴(yán)密的遞歸函數(shù)。

堆龙德庆县| 枝江市| 凤阳县| 平舆县| 庄河市| 景泰县| 安顺市| 大厂| 凤台县| 兰州市| 绍兴市| 广饶县| 稻城县| 鲜城| 林州市| 厦门市| 资中县| 曲靖市| 峨眉山市| 龙里县| 桐乡市| 体育| 甘肃省| 成武县| 沭阳县| 若羌县| 伊川县| 道真| 衡东县| 永年县| 永修县| 叶城县| 吉林市| 分宜县| 宜章县| 元谋县| 长子县| 峡江县| 青冈县| 昌黎县| 红河县|