常用排序演算法中的時間複雜度和空間複雜度
排序法 最差時間分析 平均時間複雜度 穩定度 空間複雜度
氣泡排序 O(n2) O(n2) 穩定 O(1)
快速排序 O(n2) O(n*log2n) 不穩定 O(log2n)~O(n)
選擇排序 O(n2) O(n2) 不穩定 O(1)
二叉樹排序O(n2) O(n*log2n) 不一頂 O(n)
插入排序 O(n2) O(n2) 穩定 O(1)
堆排序 O(n*log2n) O(n*log2n) 不穩定 O(1)
希爾排序 O O 不穩定 O(1)
1、時間複雜度
(1)時間頻度 一個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機執行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且一個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數多,它花費時間就多。一個演算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
(2)時間複雜度 在剛才提到的時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它變化時呈現什麼規律。為此,我們引入時間複雜度概念。 一般情況下,演算法中基本操作重複執行的次數是問題規模n的某個函式,用T(n)表示,若有某個輔助函式f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函式。記作T(n)=O(f(n)),稱O(f(n)) 為演算法的漸進時間複雜度,簡稱時間複雜度。
在各種不同演算法中,若演算法中語句執行次數為一個常數,則時間複雜度為O(1),另外,在時間頻度不相同時,時間複雜度有可能相同,如T(n)=n2+3n+4與T(n)=4n2+2n+1它們的頻度不同,但時間複雜度相同,都為O(n2)。 按數量級遞增排列,常見的時間複雜度有:常數階O(1),對數階O(log2n),線性階O(n), 線性對數階O(nlog2n),平方階O(n2),立方階O(n3),…, k次方階O(nk),指數階O(2n)。隨著問題規模n的不斷增大,上述時間複雜度不斷增大,演算法的執行效率越低。 2、空間複雜度 與時間複雜度類似,空間複雜度是指演算法在計算機內執行時所需儲存空間的度量。記作: S(n)=O(f(n)) 我們一般所討論的是除正常佔用記憶體開銷外的輔助儲存單元規模。討論方法與時間複雜度類似,不再贅述。
(3)漸進時間複雜度評價演算法時間效能 主要用演算法時間複雜度的數量級(即演算法的漸近時間複雜度)評價一個演算法的時間效能。
2、空間複雜度
類似於時間複雜度的討論,一個演算法的空間複雜度(Space Complexity)S(n)定義為該演算法所耗費的儲存空間,它也是問題規模n的函式。漸近空間複雜度也常常簡稱為空間複雜度。
空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。一個演算法在計算機儲存器上所佔用的儲存空間,包括儲存演算法本身所佔用的儲存空間,演算法的輸入輸出資料所佔用的儲存空間和演算法在執行過程中臨時佔用的儲存空間這三個方面。演算法的輸入輸出資料所佔用的儲存空間是由要解決的問題決定的,是通過引數表由呼叫函式傳遞而來的,它不隨本演算法的不同而改變。儲存演算法本身所佔用的儲存空間與演算法書寫的長短成正比,要壓縮這方面的儲存空間,就必須編寫出較短的演算法。演算法在執行過程中臨時佔用的儲存空間隨演算法的不同而異,有的演算法只需要佔用少量的臨時工作單元,而且不隨問題規模的大小而改變,我們稱這種演算法是“就地/"進行的,是節省儲存的演算法,如這一節介紹過的幾個演算法都是如此;有的演算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將佔用較多的儲存單元,例如將在第九章介紹的快速排序和歸併排序演算法就屬於這種情況。
如當一個演算法的空間複雜度為一個常量,即不隨被處理資料量n的大小而改變時,可表示為O(1);當一個演算法的空間複雜度與以2為底的n的對數成正比時,可表示為0(10g2n);當一個演算法的空I司複雜度與n成線性比例關係時,可表示為0(n).若形參為陣列,則只需要為它分配一個儲存由實參傳送來的一個地址指標的空間,即一個機器字長空間;若形參為引用方式,則也只需要為其分配儲存一個地址的空間,用它來儲存對應實參變數的地址,以便由系統自動引用實參變數。
常用的內部排序方法有:交換排序(氣泡排序、快速排序)、選擇排序(簡單選擇排序、堆排序)、插入排序(直接插入排序、希爾排序)、歸併排序、基數排序(一關鍵字、多關鍵字)。
一、氣泡排序:
1.基本思想:
兩兩比較待排序資料元素的大小,發現兩個資料元素的次序相反時即進行交換,直到沒有反序的資料元素為止。
2.排序過程:
設想被排序的陣列R[1…N]垂直豎立,將每個資料元素看作有重量的氣泡,根據輕氣泡不能在重氣泡之下的原則,從下往上掃描陣列R,凡掃描到違反本原則的輕氣泡,就使其向上"漂浮",如此反覆進行,直至最後任何兩個氣泡都是輕者在上,重者在下為止。
【示例】:
49 13 13 13 13 13 13 13
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97
二、快速排序(Quick Sort)
1.基本思想:
在 當前無序區R[1…H]中任取一個數據元素作為比較的"基準"(不妨記為X),用此基準將當前無序區劃分為左右兩個較小的無序區:R[1…I-1]和 R[I+1…H],且左邊的無序子區中資料元素均小於等於基準元素,右邊的無序子區中資料元素均大於等於基準元素,而基準X則位於最終排序的位置上,即 R[1…I-1]≤X.Key≤RI+1…H,當R[1…I-1]和R[I+1…H]均非空時,分別對它們進行上述的劃分過 程,直至所有無序子區中的資料元素均已排序為止。
2.排序過程:
【示例】:
初始關鍵字 [49 38 65 97 76 13 27 49]
第一次交換後 [27 38 65 97 76 13 49 49]
第二次交換後 [27 38 49 97 76 13 65 49]
J向左掃描,位置不變,第三次交換後 [27 38 13 97 76 49 65 49]
I向右掃描,位置不變,第四次交換後 [27 38 13 49 76 97 65 49]
J向左掃描 [27 38 13 49 76 97 65 49]
(一次劃分過程)
初始關鍵字 [49 38 65 97 76 13 27 49]
一趟排序之後 [27 38 13] 49 [76 97 65 49]
二趟排序之後 [13] 27 [38] 49 [49 65]76 [97]
三趟排序之後 13 27 38 49 49 [65]76 97
最後的排序結果 13 27 38 49 49 65 76 97
三、簡單選擇排序
1.基本思想:
每一趟從待排序的資料元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的資料元素排完。
2.排序過程:
【示例】:
初始關鍵字 [49 38 65 97 76 13 27 49]
第一趟排序後 13 [38 65 97 76 49 27 49]
第二趟排序後 13 27 [65 97 76 49 38 49]
第三趟排序後 13 27 38 [97 76 49 65 49]
第四趟排序後 13 27 38 49 [49 97 65 76]
第五趟排序後 13 27 38 49 49 [97 97 76]
第六趟排序後 13 27 38 49 49 76 [76 97]
第七趟排序後 13 27 38 49 49 76 76 [ 97]
最後排序結果 13 27 38 49 49 76 76 97
四、堆排序(Heap Sort)
1.基本思想:
堆排序是一樹形選擇排序,在排序過程中,將R[1..N]看成是一顆完全二叉樹的順序儲存結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係來選擇最小的元素。
2.堆的定義: N個元素的序列K1,K2,K3,...,Kn.稱為堆,當且僅當該序列滿足特性:
Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])
堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉子結點的關鍵字均大於等於其孩子結點的關鍵字。例如序列10,15,56,25,30,70就是一個 堆,它對應的完全二叉樹如上圖所示。這種堆中根結點(稱為堆頂)的關鍵字最小,我們把它稱為小根堆。反之,若完全二叉樹中任一非葉子結點的關鍵字均大於等 於其孩子的關鍵字,則稱之為大根堆。
3.排序過程:
堆排序正是利用小根堆(或大根堆)來選取當前無序區中關鍵字小(或最大)的記錄實現排序的。我們不妨利用大根堆來排序。每一趟排序的基本操作是:將當前無 序區調整為一個大根堆,選取關鍵字最大的堆頂記錄,將它和無序區中的最後一個記錄交換。這樣,正好和直接選擇排序相反,有序區是在原記錄區的尾部形成並逐 步向前擴大到整個記錄區。
【示例】:對關鍵字序列42,13,91,23,24,16,05,88建堆
五、直接插入排序(Insertion Sort)
- 基本思想:
每次將一個待排序的資料元素,插入到前面已經排好序的數列中的適當位置,使數列依然有序;直到待排序資料元素全部插入完為止。
- 排序過程:
【示例】:
[初始關鍵字] [49] 38 65 97 76 13 27 49
J=2(38) [38 49] 65 97 76 13 27 49
J=3(65) [38 49 65] 97 76 13 27 49
J=4(97) [38 49 65 97] 76 13 27 49
J=5(76) [38 49 65 76 97] 13 27 49
J=6(13) [13 38 49 65 76 97] 27 49
J=7(27) [13 27 38 49 65 76 97] 49
J=8(49) [13 27 38 49 49 65 76 97]
六、希爾排序
1.排序思想:
先 取一個小於n的證書d1作為第一個增量,把檔案的全部記錄分成d1組。所有距離為d1的倍數的記錄放在同一組中。先在各組內進行直接插入排序,然後取第二 個增量d2<d1重複上述的分組和排序,直到所取的增量dt=1,即所有記錄放在同一組中進行直接插入排序為止。該方法實際上是一種分組插入方法。
2.排序過程:
[初始關鍵字] 72 28 51 17 96 62 87 33 45 24
d1=n/2=5 62 28 33 17 24 72 87 51 45 96
d2=d1/2=3 17 24 33 62 28 45 87 51 72 96
d3=d2/2=1 17 24 28 33 45 51 62 72 87 96
七、歸併排序
1.排序思想:
設兩個有序的子檔案(相當於輸入堆)放在同一向量中相鄰的位置上:R[low…m],R[m+1…high],先將它們合併到一個區域性的暫存向量R1(相當於輸出堆)中,待合併完成後將R1複製回R[low…high]中。
2.排序過程:
【示例】:
初始關鍵字 [46][38][56][30][88][80][38]
第一趟歸併後[38 46][30 56][80 88][38]
第二趟歸併後[30 38 46 56][38 80 88]
最終歸併結果[30 38 38 46 56 80 88]
八、基數排序
1.排序思想:
(1)根據資料項個位上的值,把所有的資料項分為10組;
(2)然後對這10組資料重新排列:把所有以0結尾的資料排在最前面,然後是結尾是1的資料項,照此順序直到以9結尾的資料,這個步驟稱為第一趟子排序;
(3)在第二趟子排序中,再次把所有的資料項分為10組,但是這一次是根據資料項十位上的值來分組的。這次分組不能改變先前的排序順序。也就是說,第二趟排序之後,從每一組資料項的內部來看,資料項的順序保持不變;
(4)然後再把10組資料項重新合併,排在最前面的是十位上為0的資料項,然後是10位為1的資料項,如此排序直到十位上為9的資料項。
(5)對剩餘位重複這個過程,如果某些資料項的位數少於其他資料項,那麼認為它們的高位為0。
2.排序過程
【示例】
初始關鍵字 421 240 035 532 305 430 124
第一趟排序後[240 430] [421] [532] [124] [035 305]
第二趟排序後(305) (421 124) (430 532 035) (240)
最後排序結果(035) (124) (240) (305) (421 430) (532)