《演算法基礎 開啟演算法之門》一2 4 遞迴

2021-09-23 15:06:54 字數 1545 閱讀 6332

利用遞迴技術,能將乙個問題轉化為同乙個樣子問題的求解過程。這是我最喜歡的經典的遞迴例子:計算n!(「n的階乘」),它被定義為如下,對於乙個非負數n,當n=0時,n!=1且n!=n(n-1)(n-2)(n-3)…3•2•1(如果n≥1)。例如,5!=5•4•3•2•1=120。22觀察得(n-1)!=(n-1)(n-2)(n-3)…3•2•1,並且得n!=n•(n-1)!(對於n≥1)。針對n!這個問題,我們定義了「較小的」問題,也就是(n-1)!。我們能夠寫出乙個遞迴程式來計算n!,具體如下:

第2步的表達方式過於囉嗦。我可以將其改寫為「otherwise,return n•factorial(n-1)」(否則,返回nfactor2al(n-1)),即在乙個更大的算術表示式中使用遞迴呼叫的返回值。

對於遞迴,我們必須強調兩個特性。首先,必須有乙個或多個基礎情況(base case),它是指不用遞迴而直接計算出結果。第二,程式中的每個遞迴呼叫一定是通過一系列關於同一問題的子問題的求解而最終迭代到基礎情況。對於factorial程式,當n等於0時,基礎情況發生,並且每乙個遞迴呼叫最終均會將n歸約到1。只要初始時n是非負的,遞迴呼叫最終均會歸約到基礎情況。

證明遞迴演算法工作流程的第一步可能讓人感覺過於簡單。證明的關鍵是每次遞迴呼叫均會產生正確的結果。只要我們願意相信遞迴呼叫確實得到了正確結果,那麼證明正確性通常是容易的。如下是我們如何證明factorial程式返回了正確的結果。很明顯,當n=0時,結果返回1,1等價於n!。假定當n≥1時,factorial(n-1)這個遞迴呼叫返回了正確的結果:它返回了(n-1)!。該程式再用n乘以該值,因此計算出了n!這一結果,也就是最後要返回的值。

下面舉乙個程式,雖然它利用了正確的數學公式計算,但它不是基於子問題求解的遞迴呼叫。當n≥0時,確實有n!=(n+1)!/(n+1)。下列遞迴程式雖然利用了該數學公式,但是未能得出正確的結果(當n≥1時):

如果要呼叫badfactorial(1),那麼它會產生乙個遞迴呼叫badfactorial(2),該遞迴呼叫會產生乙個遞迴呼叫badfactorial(3),等等,這一系列遞迴呼叫均不會歸約到基礎情況(即n=0事件)。如果要用一種實際程式語言來實現該程式並且將該程式執行到乙個真正的計算機上,那麼你會很快就能看到一條「棧溢位錯誤」的錯誤資訊。我們通常能將演算法表示成一種遞迴風格的迴圈方式。如下是乙個線性查詢演算法,它沒有使用標記,而是使用遞迴方式書寫的:

這裡,子問題是在a[i]~a[n]這個子陣列中尋找x的問題。基礎情況發生在第1步中即當子陣列本身是空時,也就是當i>n時。因為每執行一次第3步的遞迴呼叫,如果沒有第2步中的值返回,i的值就會自增一,那麼最後i會大於n,此時我們會執行基礎情況。

《演算法基礎 開啟演算法之門》一3 2 選擇排序

現在讓我們將注意力轉向排序 重排陣列中的所有元素 也稱為重排陣列 以便每個元素小於或者等於它的後繼。我們要看到的第一種排序演算法是選擇排序,這是我能想到的最簡單的演算法,在設計乙個排序演算法時,我最先能想到的就是選擇排序,雖然它遠遠不是最快的演算法。下面我們用依據作者名字對書架上的書排序這個例子來說...

《演算法基礎 開啟演算法之門》一2 3 迴圈不變式

對於線性查詢的3個演算法,我們能很容易地看到每個演算法均能生成正確的結果。但是有時候生成正確的結果看起來有點難。這涉及一系列技術,在這裡不能一一講解。證明正確性的乙個常用方法是使用迴圈不變式證明 即證明迴圈的每次迭代之前迴圈不變式為真。迴圈不變式能夠幫助我們證明正確性,關於迴圈不變式,我們必須證明以...

基礎演算法 遞迴 一

1.漢諾塔 include using namespace std void fac int n,char a,char b,char c int main 2.普通揹包問題 設有乙個揹包可以放入的物品重量為s,現有n件物品,重量 分別是w1 w2,w3 wn 問能否從這 n件物品中選擇若干件 放入...