排序的定義:輸入:n個數:a1,a2,a3,...,an輸出:n個數的排列:a1',a2',a3',...,an',使得a1'<=a2'<=a3'<=...<=an'。In-place sort(不占用額外內存或占用常數的內存):插入排序、選擇排序、冒泡排序、堆排序、快速排序。Out-place sort:歸并排序、計數排序、基數排序、桶排序。當需要對大量數據進行排序時,In-place sort就顯示出優點,因為只需要占用常數的內存。設想一下,如果要對10000個數據排序,如果使用了Out-place sort,則假設需要用200G的額外空間,則一臺老式電腦會吃不消,但是如果使用In-place sort,則不需要花費額外內存。stable sort:插入排序、冒泡排序、歸并排序、計數排序、基數排序、桶排序。unstable sort:選擇排序(5 8 5 2 9)、快速排序、堆排序。為何排序的穩定性很重要?在初學排序時會覺得穩定性有這么重要嗎?兩個一樣的元素的順序有這么重要嗎?其實很重要。在基數排序中顯得尤為突出,如下:
算法導論習題8.3-2說:如果對于不穩定的算法進行改進,使得那些不穩定的算法也穩定?其實很簡單,只需要在每個輸入元素加一個index,表示初始時的數組索引,當不穩定的算法排好序后,對于相同的元素對index排序即可。基于比較的排序都是遵循“決策樹模型”,而在決策樹模型中,我們能證明給予比較的排序算法最壞情況下的運行時間為Ω(nlgn),證明的思路是因為將n個序列構成的決策樹的葉子節點個數至少有n!,因此高度至少為nlgn。線性時間排序雖然能夠理想情況下能在線性時間排序,但是每個排序都需要對輸入數組做一些假設,比如計數排序需要輸入數組數字范圍為[0,k]等。在排序算法的正確性證明中介紹了”循環不變式“,他類似于數學歸納法,"初始"對應"n=1","保持"對應"假設n=k成立,當n=k+1時"。
改進版冒泡排序最佳運行時間:O(n)最壞運行時間:O(n^2)
證明算法正確性:循環不變式:A[1...i-1]包含了A中最小的i-1個元素,且已排序。初始:i=1,A[1...0]=空,因此成立。保持:在某次迭代開始之前,保持循環不變式,即A[1...i-1]包含了A中最小的i-1個元素,且已排序,則進入循環體后,程序從 A[i...n]中找出最小值放在A[i]處,因此A[1...i]包含了A中最小的i個元素,且已排序,而i++,因此下一次循環之前,保持 循環不變式:A[1..i-1]包含了A中最小的i-1個元素,且已排序。終止:i=n,已知A[1...n-1]包含了A中最小的i-1個元素,且已排序,因此A[n]中的元素是最大的,因此A[1...n]已排序,證畢。算法導論2.2-2中問了"為什么偽代碼中第3行只有循環n-1次而不是n次"?在循環不變式證明中也提到了,如果A[1...n-1]已排序,且包含了A中最小的n-1個元素,則A[n]肯定是最大的,因此肯定是已排序的。遞歸版選擇排序
遞歸式:T(n)=T(n-1)+O(n) => T(n)=O(n^2)
證明算法正確性:其實我們只要證明merge()函數的正確性即可。merge函數的主要步驟在第25~31行,可以看出是由一個循環構成。循環不變式:每次循環之前,A[p...k-1]已排序,且L[i]和R[j]是L和R中剩下的元素中最小的兩個元素。初始:k=p,A[p...p-1]為空,因此已排序,成立。保持:在第k次迭代之前,A[p...k-1]已經排序,而因為L[i]和R[j]是L和R中剩下的元素中最小的兩個元素,因此只需要將L[i]和R[j]中最小的元素放到A[k]即可,在第k+1次迭代之前A[p...k]已排序,且L[i]和R[j]為剩下的最小的兩個元素。終止:k=q+1,且A[p...q]已排序,這就是我們想要的,因此證畢。歸并排序的例子:
問:歸并排序的缺點是什么?答:他是Out-place sort,因此相比快排,需要很多額外的空間。問:為什么歸并排序比快速排序慢?答:雖然漸近復雜度一樣,但是歸并排序的系數比快排大。問:對于歸并排序有什么改進?答:就是在數組長度為k時,用插入排序,因為插入排序適合對小數組排序。在算法導論思考題2-1中介紹了。復雜度為O(nk+nlg(n/k)) ,當k=O(lgn)時,復雜度為O(nlgn)
證明算法正確性:(1)證明build_max_heap的正確性:循環不變式:每次循環開始前,A[i+1]、A[i+2]、...、A[n]分別為最大堆的根。初始:i=floor(n/2),則A[i+1]、...、A[n]都是葉子,因此成立。保持:每次迭代開始前,已知A[i+1]、A[i+2]、...、A[n]分別為最大堆的根,在循環體中,因為A[i]的孩子的子樹都是最大堆,因此執行完MAX_HEAPIFY(A,i)后,A[i]也是最大堆的根,因此保持循環不變式。終止:i=0,已知A[1]、...、A[n]都是最大堆的根,得到了A[1]是最大堆的根,因此證畢。(2)證明heapsort的正確性:循環不變式:每次迭代前,A[i+1]、...、A[n]包含了A中最大的n-i個元素,且A[i+1]<=A[i+2]<=...<=A[n],且A[1]是堆中最大的。初始:i=n,A[n+1]...A[n]為空,成立。保持:每次迭代開始前,A[i+1]、...、A[n]包含了A中最大的n-i個元素,且A[i+1]<=A[i+2]<=...<=A[n],循環體內將A[1]與A[i]交換,因為A[1]是堆中最大的,因此A[i]、...、A[n]包含了A中最大的n-i+1個元素且A[i]<=A[i+1]<=A[i+2]<=...<=A[n],因此保持循環不變式。終止:i=1,已知A[2]、...、A[n]包含了A中最大的n-1個元素,且A[2]<=A[3]<=...<=A[n],因此A[1]<=A[2]<=A[3]<=...<=A[n],證畢。
新聞熱點
疑難解答