來回翻閱快速排序相關算法介紹, 真是感嘆大師們的大智慧 ~ 這裏經過練習的角度帶你們手寫個玩具. 一塊兒樂一樂, 當課外做業.node
大街上的快速排序練習做業偏地都是, 這裏也是基於相似的策略去構建.github
基準(軸)策略是快排的核心, 它決定最終的算法複雜度. 有的篩法策略能保證算法複雜度穩定在 O(nlogn). 多數工程採用策略都在 O(nlogn) ~ O(n^2) 區間, 但算法複雜度指望穩定在 O(nlogn). 這裏折中選擇了三數取中策略, 對有序數組有不差的效果表現, 算法複雜度仍然在 O(nlogn) ~ O(n^2) 區間內.算法
根據數據量和遞歸深度來切換使用, 簡單交換排序, 仍是插入排序, 仍是堆排序等策略.數組
{ 等於 | 小於 | 大於 | 等於 } -> { 小於 | 等於 | 大於 } 用於剔出重複元素. 對於重複數據較多時候效果好.ui
static inline void swap_if_great(int * a, int * b) { if (*a > *b) swap(a, b); } /** * low, mid, high 三個未知上數據, 選取他們中間數據做爲軸驅 * mid = low + ((high - low) >> 1) */ static inline int quick_pivot(int * low, int * high) { int * mid = low + ((high - low) >> 1); swap_if_great(mid, high); swap_if_great(low, high); swap_if_great(mid, low); // 此時 *mid < *low < *high // low 就是三個位置的中值 return *low; } #define INT_SORT_INSERT (16) static inline bool quick_sort_before(int * low, int * high, int depth) { ptrdiff_t n = high - low; if (n <= INT_SORT_INSERT) { switch (n) { case 0: case 1: return true; case 2: swap_if_great(low, high); return true; case 3: swap_if_great(low, low+1); swap_if_great(low, high); swap_if_great(low+1, high); return true; default: insert_sort(low, high); return true; } } if (depth <= 0) { heap_sort(low, n); return true; } return false; } static void quick_sort_partial(int * low, int * high, int depth) { if (quick_sort_before(low, high, depth)) return; // 開始 quick sort pivot 分割 int pivot = quick_pivot(low, high); // 嘗試三路分割 { 等於 | 小於 | 大於 | 等於 | } -> { 小於 | 等於 | 大於 } // // -------------------------------------- // | 等於 | 小於 | ... | 大於 | 等於 | // + + + + + + // low p i j q high // -------------------------------------- // // ------------------------- // | 小於 | 等於 | 大於 | // + + + + // low i j high // ------------------------- // // p 指向序列左邊等於 pivot 元素的位置 // q 指向序列右邊等於 pivot 元素的位置 // i 指向從左到右掃描的元素位置 // j 指向從右往左掃描的元素位置 // // int * p = low, * q = high, * i = low, * j = high; do { while (i < j && *j >= pivot) { // 找到與錨點相等的元素, 因而交換到 q 所指向的位置 if (*j == pivot) swap(j, q--); j--; } *i = *j; while (i < j && *i <= pivot) { // 找到與錨點相等的元素, 因而交換到 q 所指向的位置 if (*i == pivot) swap(i, p++); i++; } *j = *i; } while (i < j); *i = pivot; print(low, high - low + 1); // 開始處理兩邊重複元素, 將其交換到序列中間 if (i == p) i = low - 1; else { i--; while (--p >= low) swap(i--, p); } if (j == q) j = high + 1; else { j++; while (++q <= high) swap(j++, q); } print(low, high - low + 1); // 開始遞歸處理 quick_sort_partial(low, i, depth - 1); // 尾遞歸 quick_sort_partial(j, high, depth); } #define INT_QUCICK_SORT_DEPATH (32) void quick_sort(int * a, int n) { if (a && n > 1) quick_sort_partial(a, a + n - 1, INT_QUCICK_SORT_DEPATH); }
#include <stddef.h> #include <stdbool.h> static inline void swap(int * a, int * b) { int t = *a; *a = *b; *b = t; } static void heap_adjust(int * a, ptrdiff_t n, ptrdiff_t i) { int node = a[i]; for (ptrdiff_t j = 2*i+1; j < n; j = 2*i+1) { if (j+1 < n && a[j] < a[j+1]) j++; // 已是大頂堆直接結束 if (a[j] <= node) break; a[i] = a[j]; i = j; } a[i] = node; } static void heap_sort(int * a, ptrdiff_t n) { // 構建大頂堆 for (ptrdiff_t i = n/2-1; i >= 0; i--) heap_adjust(a, n, i); // 數據交換 while (--n > 0) { // 堆頂元素與末尾元素進行交換 swap(a, a + n); // 從新對堆進行調整 heap_adjust(a, n, 0); } } static void insert_sort(int * low, int * high) { for (int * lo = low + 1; lo <= high; lo++) { int v = *lo, * hi = lo - 1; if (v < *hi) { do { hi[1] = *hi; } while (--hi >= low && v < *hi); hi[1] = v; } } }
玩具好寫, 工程實戰級別仍是挺有挑戰的. 技術的修行路途漫長. 代碼倉促寫完其實很不滿意, 奈何功力不夠, 下次有感悟再來浴火重生. 很是歡迎朋友交流指教. 祝好運, bye ~指針