今天北京下了一場雨,半夜電閃雷鳴的,又有多少人膽戰心驚了呢。
遞歸:遞歸過程的執行總是一個過程體未執行完,就帶着本次執行的結果進入到另一輪過程體的執行中……如此反覆,不斷深入,直到某次過程的執行遇到終止遞歸調用的條件成立時,則不再深入,而執行本次的過程體餘下的部分,然後返回到上一次調用的過程體中,執行餘下的部分……如此反覆,直到回到起始位置上,得到相應的程序運行結果。
可以說,遞歸過程的程序設計的核心思想就是參照這種執行流程,設計出一種適合“逐步深入,而後又逐步返回”的遞歸調用模型,以解決實際的問題!
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
三個步驟:
step1 分解:將原問題分解爲若干個規模較小,相互獨立,與原問題形式相同的子問題;
step2 解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題
step3 合併:將各個子問題的解合併爲原問題的解。
No1:斐波那契數列(Fibonacci數列)
問題描述:一個數列,當n=0或1時,F(n)=1;當n>1時,F(n)=F(n-1)+F(n-2);示例如:1、1、2、3、5、8、13……
問題分析:這是一個可以用簡單的遞歸方程式解決的問題。通過這個數列的規律,即可馬上得到這個數列的遞歸關係式,我們先定義F(n)爲該數列中下標爲n的那個數的值。
圖1-1
顯而易見地可以看出該數列存在的原問題的子問題。當自變量爲某一個值時,所求結果能爲一個固定數值,則可看做一個遞歸的出口,當自變量爲某一值時,結果爲由比原問題更簡單的子問題組成,並且子問題的性質與原問題相同,只是規模更小,在此遞歸求解。最後程序到達出口處完成輸出即爲原問題的解。
參考答案:原問題F(n)爲斐波那契數列的第n個數的值;
子問題爲當n>1時,F(n)=F(n-1)+F(n-2);
當n=0或1時,F(n)=1。
遞歸出口爲F(0)=F(1)=1。
注意:(最好給出遞歸關係式,就是上圖1-1那種形式的方程式。然後三要素:原問題用什麼表示、子問題的劃分、最後要給出函數終止的條件)
No2:漢諾塔問題
問題描述:從1到n編號的盤子,其編號越大則盤子的大小越大。有三根柱子A,B,C,盤子能放在柱子上,並且只能是小盤子放在大盤子之上。漢諾塔問題正是求解如何將n個盤子從A柱藉助B柱子移動到C柱。
問題分析:這是一個典型的適用遞歸描述的問題。通過分析,我們可以知道,原問題可以描述爲F(start_panzi,end_panzi,start_zhuzi, center_zhuzi, end_zhuzi),翻譯爲:
F(最小的盤號,最大的盤號,原先所在的柱子,能夠藉助的柱子,最終要移動到的柱子)
如F(1,3,A,B,C)爲把從1號到3號盤子,從A柱藉助B柱移到C柱。
我們能夠發現現在我們就能把原問題劃分成3個步驟完成,先把從1到n-1個盤子從A藉助C移動到B,再把第n個盤子直接移動到C,最後把從1到n-1個盤子從B藉助A移動到C即可。
圖1-2
參考答案:原問題F(sp,ep,sz,cz,ez)爲將編號從sp到ep的所有盤子從sz柱,藉助cz柱,移動到ez柱。
子問題爲將原問題劃分爲3個步驟來完成:第一步將最後一個盤子不動,即將sp到ep-1號的盤子從sz柱藉助ez暫時放到cz中間柱,再把第ep個盤子直接從sz放到ez柱上去,最後再把放在中間柱cz上的sp到ep-1盤從cz藉助sz移動到ez就完成了。
遞歸出口爲當問題中只有一個盤子的時候就只需要將該盤從所在柱子直接放到目標柱子(即子問題拆分的第2步)。
注:最好寫出上圖1-2中的遞歸關係式。
總結:解決遞歸問題,只需分析得出原問題+子問題+遞歸出口。如果在能寫出遞歸關係式的情況下,儘量給出,遞歸關係式能言簡意賅的讓別人明白拆分步驟,而且能直接簡單的轉換爲相對應的代碼。
附1:二分搜索問題
參考答案:原問題爲Search(low,high,num)在已排序的數組下標爲low到high之間尋找num元素。默認爲從小到大的有序數組。
子問題劃分爲首先取low到high最中間的那個元素,即下標爲center=(low+high)/2的元素記爲mmp,如果等於num則返回表示已找到,如果mmp<num則向右區間搜索(center+1到high之間),如果mmp>num則向左區間搜索(low到center-1之間)。
遞歸出口爲mmp==num的時候表示已找到,則正常退出。還有當遍歷整個數組還沒有找到則退出,表示該數組中並沒有num這個數。
遞歸關係式:圖1-3