現在讓我們將注意力轉向排序:重排陣列中的所有元素——也稱為重排陣列——以便每個元素小於或者等於它的後繼。我們要看到的第一種排序演算法是選擇排序,這是我能想到的最簡單的演算法,在設計乙個排序演算法時,我最先能想到的就是選擇排序,雖然它遠遠不是最快的演算法。
下面我們用依據作者名字對書架上的書排序這個例子來說明選擇排序是如何執行的。從左向右查詢整個書架,並且找到作者名字最先在字母表**現的書籍。假定這本排序在字母表中最前的書籍是由louisa may alcott所寫的(如果書架上包含由該作者所寫的兩本或者兩本以上的書籍,選擇它們中的任意一本)。將這個位置上的書籍和初始時位於位置1上的書籍進行調換。32現在位於位置1上的書籍是作者名字最先在字母表**現的書籍。現在沿著書架從左向右查詢,查詢從第2個位置到第n個位置上的書籍中最先在字母表**現的書籍。並假定這本書是由jane austen所寫的。將這本書與位於第2個位置的書籍進行調換,從而使得現在位於第1個位置和第2個位置上的書籍同時也是按照字母表排序中的位於最前面的第1個、第2個書籍。同理操作,得到位置3上應該放置的書籍,等等。一旦在位置n-1處放置了正確的書籍(可能是由hgwells所寫的書),那麼我們就完成操作了,因為這時僅僅就剩下一本書了(比如說,由oscar wilde所寫的書),並且它就位於它本身應該放置的第n個位置處。
為了將這個方法轉換成乙個計算機演算法,可以將書架看作是乙個陣列,所有的書看作是陣列中的所有元素。下面是這個程式。
在a[in]中查詢最小的元素相當於線性查詢的變體。首先宣告a[i]是目前所看到的子陣列中最小的元素,然後掃瞄子陣列的剩餘部分,每當發現有乙個元素小於當前最小的元素時,我們就更新最小元素的索引。下面是重定義的程式。
該程式有個「巢狀」迴圈,即第1b步中的迴圈巢狀在第1步中。對於外層迴圈的每次迭代,內層迴圈會執行它的迴圈體內的所有迴圈。注意內層迴圈的初始值j依賴於外層迴圈的當前值i。下圖表明了選擇排序在乙個包含6個元素的陣列中是如何進行排序的:
左上方是初始陣列,每步展示了經過外層迴圈的一次迭代後的結果。灰色陰影的元素是當前得到的排好序的子陣列。如果你想使用迴圈不變式來證明selectionsort程式能正確地實現排序操作,那就需要對每個迴圈進行證明。程式太簡單,我們不再證明其正確性,迴圈不變式如下:第1步中的每次迴圈迭代開始時,子陣列a[1i-1]儲存著整個陣列a的前i-1個有序排列的最小元素。第1b步中的每次迴圈迭代開始時,a[smallest]中存放著子陣列a[ij-1]中的最小元素。selectionsort的執行時間是多少?我們將證明它是θ(n2)。關鍵是分析出內層迴圈執行了多少次迭代,其中每次迭代需要花費θ(1)時間。(這裡,因為在每次迭代中對smallest的賦值操作可能發生也可能不會發生,因此θ符號的下界和上界中的常數因子可能是不同的。)讓我們基於外部迴圈中迴圈變數i的值計算迭代的次數。當i等於1時,內層迴圈中j從2變化到n,共執行n-1次。當i等於2時,內層迴圈中j從3到n,共執行n-2次。外層迴圈中i值每增加1,那麼內層迴圈執行次數會減少1次。總結可得,內層迴圈每次執行n-i次。在外層迴圈的最後一次迭代中,當i等於n-1時,內層迴圈僅僅執行1次。因此內層迴圈迭代的總數是(n-1)+(n-2)+(n-3)+…+2+1,34這就是乙個等差數列求和。根據等差數列的基本公式:對於任意非負整數k有
用n-1代替k,我們看到內層迴圈迭代的總次數是(n-1)n/2,即(n2-n)/2。使用漸近符號來省略低階項(-n)和常數因子(1/2),那麼我們就可以稱內層迴圈的總次數是θ(n2)。因此,selectionsort的執行時間是θ(n2)。注意該執行時間能夠覆蓋所有情況。無論實際的元素值是什麼,內層迴圈均會執行θ(n2)次。下面用另一種不使用等差數列的方法來證明執行時間是θ(n2)。我們將分別證明執行時間是o(n2)和ω(n2),將漸近上界和漸近下界聯合考慮就會得到θ(n2)。為了證明執行時間是o(n2),我們觀察發現外部迴圈的每次迭代對應的內層迴圈最多執行n-1次,而每次內層迴圈的迭代需花費常量時間,故外層迴圈的每次迭代需花費o(n)時間。由於外部迴圈迭代n-1次,即也是o(n),故內層迴圈需要花費的總執行時間是o(n)乘以o(n),即o(n2)。為了證明執行時間是ω(n2),我們觀察發現在外層迴圈的前n/2次迭代中,每個內層迴圈至少迭代n/2次,那麼總執行次數至少為n/2乘以n/2,即n2/4次。由於每個內層迴圈花費常量時間,所以我們證明出了執行時間至少是n2/4的常數倍,即為ω(n2)。最後總結一下關於選擇排序的兩個結論。首先,我們看到漸近執行時間為θ(n2),這是我們觀察到的最壞的排序演算法。第二,如果認真觀察選擇排序是如何執行的,你將發現θ(n2)的執行時間來自於第1bi步中的比較操作。但是移動元素的次數僅僅是θ(n),因為第1c步僅僅執行了n-1次。如果移動元素相當耗時——或者它們所佔空間很大或者它們儲存在乙個儲存較慢的裝置(例如磁碟)中——那麼選擇排序可能是乙個合適的演算法。
《演算法基礎 開啟演算法之門》一2 4 遞迴
利用遞迴技術,能將乙個問題轉化為同乙個樣子問題的求解過程。這是我最喜歡的經典的遞迴例子 計算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 3 迴圈不變式
對於線性查詢的3個演算法,我們能很容易地看到每個演算法均能生成正確的結果。但是有時候生成正確的結果看起來有點難。這涉及一系列技術,在這裡不能一一講解。證明正確性的乙個常用方法是使用迴圈不變式證明 即證明迴圈的每次迭代之前迴圈不變式為真。迴圈不變式能夠幫助我們證明正確性,關於迴圈不變式,我們必須證明以...
基礎演算法之選擇排序演算法
在要排序的一組數中,選出最小 或者最大 的乙個數與第1個位置的數交換 然後在剩下的數當中再找最小 或者最大 的與第2個位置的數交換,依次類推,直到第n 1個元素 倒數第二個數 和第n個元素 最後乙個數 比較為止。def select sort array for i in range len arr...