2. 排序演算法基礎
1. 引言
我們通常所說的排序演算法往往指的是內部排序演算法,即資料記錄在記憶體中進行排序。排序演算法大體可分為兩種:
比較排序:主要有氣泡排序,選擇排序,插入排序,歸併排序,堆排序,快速排序等,時間複雜度為O(nlogn) ~ O(n^2)
非比較排序:主要有計數排序,基數排序,桶排序等,時間複雜度可以為O(n)
2. 比較排序
下表給出了常見比較排序演算法的效能:
1. 排序演算法的穩定性
排序演算法穩定性的簡單形式化定義為:如果Ai = Aj,排序前Ai在Aj之前,排序後Ai還在Aj之前,則稱這種排序演算法是穩定的。通俗地講就是保證排序前後兩個相等的數的相對順序
不變。對於不穩定的排序演算法,只要舉出一個例項,即可說明它的不穩定性;而對於穩定的排序演算法,必須對演算法進行分析從而得到穩定的特性。需要注意的是,排序演算法是否為穩
定的是由具體演算法決定的,不穩定的演算法在某種條件下可以變為穩定的演算法,而穩定的演算法在某種條件下也可以變為不穩定的演算法。例如,對於氣泡排序,原本是穩定的排序演算法,
如果將記錄交換的條件改成A[i] >= A[i + 1],則兩個相等的記錄就會交換位置,從而變成不穩定的排序演算法。
排序演算法穩定性的好處:排序演算法如果是穩定的,那麼從一個鍵上排序,然後再從另一個鍵上排序,前一個鍵排序的結果可以為後一個鍵排序所用。基數排序就是這樣,先按低位
排序,逐次按高位排序,低位排序後元素的順序在高位也相同時是不會改變的。
2. 氣泡排序
氣泡排序是最基礎的排序演算法,它重複地訪問要排序的元素,依次比較相鄰兩個元素大小,如果他們的順序錯誤就把他們調換過來,直到沒有元素再需要交換,排序完成。這個算
法的名字的由來是因為越小(或越大)的元素會經由交換慢慢“浮”到數列的頂端。
實現步驟:
1. 比較相鄰的元素,如果前一個比後一個大,則調換它們的位置;
2. 對每一對相鄰元素做同樣的工作,從開始第一對到結尾最後一對,每次最大的元素會依次排列到最後;
3. 針對所有的元素重複以上的步驟,除了已經排好序的元素,直到沒有任何一對元素需要比較。
程式碼實現:
#include <stdio.h> // 分類 -------------- 內部比較排序 // 資料結構 ---------- 陣列 // 最差時間複雜度 ---- O(n^2) // 最優時間複雜度 ---- 如果能在內部迴圈第一次執行時,使用一個旗標來表示有無需要交換的可能,可以把最優時間複雜度降低到O(n)// 平均時間複雜度 ---- O(n^2) // 所需輔助空間 ------ O(1) // 穩定性 ------------ 穩定 void Swap(int A[], int i, int j) { int temp = A[i]; A[i] = A[j]; A[j] = temp; } void BubbleSort(int A[], int n) { for (int j = 0; j < n - 1; j++) // 每次最大元素就像氣泡一樣"浮"到陣列的最後 { for (int i = 0; i < n - 1 - j; i++) // 依次比較相鄰的兩個元素,使較大的那個向後移 { if (A[i] > A[i + 1]) // 如果條件改成A[i] >= A[i + 1],則變為不穩定的排序演算法 { Swap(A, i, i + 1); } } } } int main() { int A[] = { 6, 5, 3, 1, 8, 7, 2, 4 }; // 從小到大氣泡排序 int n = sizeof(A) / sizeof(int); BubbleSort(A, n); printf("氣泡排序結果:"); for (int i = 0; i < n; i++) { printf("%d ", A[i]); } printf("\n"); return 0; }