手寫玩具 - 快速排序

手寫玩具 - 快速排序

來回翻閱快速排序相關算法介紹, 真是感嘆大師們的大智慧 ~ 這裏經過練習的角度帶你們手寫個玩具. 一塊兒樂一樂, 當課外做業.node

引用

1. quick sort source codegit

算法實現的相關策略

大街上的快速排序練習做業偏地都是, 這裏也是基於相似的策略去構建.github

策略1: 指針代替索引

策略2: 基準策略採用三數取中

基準(軸)策略是快排的核心, 它決定最終的算法複雜度. 有的篩法策略能保證算法複雜度穩定在 O(nlogn). 多數工程採用策略都在 O(nlogn) ~ O(n^2) 區間, 但算法複雜度指望穩定在 O(nlogn). 這裏折中選擇了三數取中策略, 對有序數組有不差的效果表現, 算法複雜度仍然在 O(nlogn) ~ O(n^2) 區間內.算法

策略3: 排序算法多維度混合策略

根據數據量和遞歸深度來切換使用, 簡單交換排序, 仍是插入排序, 仍是堆排序等策略.數組

策略4: 三路分割

{ 等於 | 小於 | 大於 | 等於 } -> { 小於 | 等於 | 大於 } 用於剔出重複元素. 對於重複數據較多時候效果好.ui

策略5: 尾遞歸

核心代碼

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 ~指針

相關文章
相關標籤/搜索