快速排序是最流行的,也是速度最快的排序算法(C++ STL 的sort函數就是實現的快速排序); 快速排序(Quicksort)是對冒泡排序的一種改進。由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據序列變成有序序列。其算法的特點就是有一個樞軸(pivot), 樞軸左邊的元素都小於/等於樞軸所指向的元素, 樞軸右邊的元素都大於樞軸指向的元素;
快速排序算法思想:
設要排序的數組是A[0], ..., A[N-1],首先任意選取一個數據作爲standard(通常選用數組的最後一個數)作爲關鍵數據,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面(其實只要保證所有比他小的元素都在其前面,則後一條件則自動滿足了),這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。(信息來源:百度百科)
一次劃分
目標:
找一個記錄,以它的關鍵字/下標作爲”樞軸/pivot”,凡是值小於樞軸的元素均移動至該樞軸所指向的記錄之前,凡關鍵字大於樞軸的記錄均移動至該記錄之後。
致使一趟排序之後,記錄的無序序列R[s..t]將分割成兩部分:R[s..i-1]和R[i+1..t],且
R[j].value ≤ R[i].value ≤ R[j].value
- //實現
- template <typename Type>
- int partitionBy3Loop(Type *array, int p, int r)
- {
- int i = p;
- int j = r+1; //j:超出末尾元素額下一位置
- Type x = array[p]; //將最左邊的元素作爲樞軸元素
- //將<x的元素交換到左邊區域
- //將>x的元素交換到右邊區域
- while (true)
- {
- //找到一個比x大(>=x)的元素
- while (i < r && array[++i] < x);
- //找到一個比x小(<=x)的元素
- while (array[--j] > x);
- if (i >= j)
- break;
- //交換
- std::swap(array[i], array[j]);
- }
- //將樞軸元素與array[p]進行交換
- std::swap(array[p], array[j]);
- //返回樞軸
- return j;
- }
- /**說明:
- 幾乎國內所有的數據結構與算法的教材中的Partition實現都
- 類似於上面的那一種, 雖然易於理解,但實現過於複雜;
- <算法導論>中給出了另一種實現方式,
- 該方式雖然不易於理解(其實明白其原理之後你就會愛上她),但是比較容易實現!
- */
- template <typename Type>
- int partitionBy1Loop(Type *array, int p, int r)
- {
- Type x = array[r]; //x作爲最終樞軸所指向的元素
- //i指向的是樞軸左邊的最後一個元素
- //也就是與x左鄰元素的下標
- int i = p - 1;
- //j則不斷的尋找下一個<=x的元素
- for (int j = p; j < r; ++j)
- {
- if (array[j] <= x)
- {
- ++ i;
- std::swap(array[i], array[j]);
- }
- }
- std::swap(array[i+1], array[r]);
- //最終使得所有(i+1)左邊的元素都<=array[i+1],
- //因此, 所有array[i+2:r]的元素都是大於array[i+1]的
- return i+1;
- }
快速排序
首先對無序的記錄序列進行“一次劃分”,之後分別對分割所得兩個子序列“遞歸”進行快速排序。
- //實現
- template <typename Type>
- void quickSort(Type *array, int p, int r)
- {
- if (p < r)
- {
- int pivot = partitionBy1Loop(array, p, r);
- quickSort(array, p, pivot-1);
- quickSort(array, pivot+1, r);
- }
- }
快速排序的時間複雜性
假設一次劃分所得樞軸位置 i = k,則對 n 個記錄進行快排所需時間:
T(n) = {Tpass(n) + T(k-1) + T(n-k) |Tpass(n)爲對 n 個記錄進行一次劃分所需時間}
若待排序列中記錄的關鍵字是隨機分佈的,則 k 取 1 至 n 中任意一值的可能性相同。
由此可得快速排序所需時間的平均值爲:
設 Tavg(1)≤b,則可得結果:
因此:快速排序的時間複雜度爲O(nlogn)
若待排記錄的初始狀態爲按關鍵字有序時,快速排序將蛻化爲起泡排序,其時間複雜度爲O(n^2)。
爲避免出現這種情況,需在進行一次劃分之前,進行“預處理”,即:先對 R(s).key, R(t).key 和 R[ë(s+t)/2û].key,進行相互比較,然後取關鍵字爲三個元素中居中間的那個元素作爲樞軸記錄。