1. 程式人生 > 實用技巧 >C語言快速排序

C語言快速排序

原理

將未排序元素根據一個作為基準的"主元"(Pivot)分為兩個子序列, 其中一個子序列的記錄均大於主元, 而另一個子序列均小於主元, 然後遞迴地對這兩個子序列用類似的方法進行排序. 本質上, 快速排序使用分治法, 將問題的規模減小, 然後再分別進行處理.

子序列的劃分方法是, 從原序列中選擇一個主元, 將比主元大的元素從右向左放置, 而比主元小的元素從左向右放置.

兩個問題

一個問題是, 為了避免最壞結果, 在確定主元時需要有一定技巧. 一種比較好的方法是, 將 A[Low](見下文的具體程式碼)、A[High]、A[(Low + High) / 2]三者關鍵字的中值作為主元, 這樣有可能避免在基本有序的序列中進行快速排序時時間複雜度出現最壞情況的問題.

另一個問題時, 由於快速排序一般是用遞迴實現的, 如果待排序列的規模比較小, 遞迴的副作用就會凸顯出來, 效果甚至還不如簡單的插入排序. 所以更專業一點的處理, 是在遞迴過程中檢查當前子問題的規模, 當其小於某個閾值時就不繼續遞迴, 而是直接呼叫插入排序解決子問題.

程式碼實現

#include <stdio.h>

#define Cutoff 3 // 定義一個閾值, 如果小於這個值, 就進行插入排序, 而不是快排

typedef int ElementType;

void Swap(ElementType *a, ElementType *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

ElementType Median3(ElementType A[], int Left, int Right)
{
    int Center = (Left + Right) / 2;
    if (A[Left] > A[Center])
        Swap(&A[Left], &A[Center]);
    if (A[Left] > A[Right])
        Swap(&A[Left], &A[Right]);
    if (A[Center] < A[Right])
        Swap(&A[Center], &A[Right]);
    /* 此時 A[Left] <= A[Center] <= A[Right] */
    Swap(&A[Center], &A[Right - 1]); // 將基準 Pivot 藏到右邊
    // 只需要考慮 A[Left + 1] ... A[Right - 2]
    return A[Right - 1]; // 返回基準 Pivot
}

/**
 * 插入排序
 * @param A 待排陣列 
 * @param N 陣列 size
 */
void InsertionSort(ElementType A[], int N)
{
    int P, i;
    ElementType Tmp;

    for (P = 1; P < N; P++)
    {
        Tmp = A[P]; // 取出未排序序列中的第一個元素
        for (i = P; i > 0 && A[i - 1] > Tmp; i--)
            A[i] = A[i - 1]; // 依次與已排序序列中元素比較並右移
        A[i] = Tmp; // 放進合適的位置
    }
}

/**
 * 核心遞迴函式
 * @param A 待排陣列
 * @param Left 陣列左邊界
 * @param Right 陣列右邊界
 */
void QSort(ElementType A[], int Left, int Right)
{
    int Pivot, Low, High;

    if (Cutoff <= Right - Left)
    {
        Pivot = Median3(A, Left, Right); // 選基準
        Low = Left;
        High = Right - 1;

        // 將序列中比基準小的移到基準左邊, 大的移到右邊
        while (1)
        {
            while (A[++Low] < Pivot);
            while (A[--High] > Pivot);
            if (Low < High)
                Swap(&A[Low], &A[High]);
            else
                break;
        }
        Swap(&A[Low], &A[Right - 1]); // 將基準交換到正確的位置
        QSort(A, Left, Low - 1); // 遞迴解決左邊
        QSort(A, Low + 1, Right); // 遞迴解決右邊
    } else
    {
        InsertionSort(A + Left, Right - Left + 1); // 元素太少, 採用簡單排序
    }
}

/**
 * 統一介面
 * @param A 待排陣列
 * @param N 元素 size
 */
void QuickSort(ElementType A[], int N)
{
    QSort(A, 0, N - 1);
}

// 測試
int main()
{
    int A[11] = {44,12,59,36,62,43,94,7,35,52,85};

    QuickSort(A, 11);

    for (int i = 0; i < 11; i++)
    {
        printf("%d ", A[i]);
    }

    return 0;
}

輸出結果:

7 12 35 43 36 59 62 52 85 44 94 

參考: 浙江大學資料結構