1. 程式人生 > >資料結構與演算法C++之三路快速排序

資料結構與演算法C++之三路快速排序

前兩篇部落格介紹了快速排序演算法以及對快速排序演算法的兩種改進
下面開始介紹三路快速排序演算法,之所以稱為三路快速排序演算法,是因為其考慮了三個部分(如下圖),分別為大於 v v 的部分, 小於 v v 的部分, 等於 v

v 的部分。
在這裡插入圖片描述
如上圖所示,隨機選擇一個元素 v v 作為參考元素,並與最左邊元素交換位置,交換後參考元素 v v 的索引為 l
l
. 然後定義小於 v v 的元素中最後一個元素索引為 l t lt ,大於 v v 的元素中第一個元素索引為 g t gt ,當前處理元素索引為 i i ,三路排序就是使
a r r [ l + 1... l t ] < v arr[l+1...lt]<v , a r r [ g t . . . r ] > v arr[gt...r]>v , a r r [ l t + 1... i 1 ] = = v arr[lt+1...i-1]==v
(1)當前元素 i i 大於 v v 時, s w a p ( a r r [ i ] , a r r [ g t 1 ] ) swap(arr[i], arr[gt-1]) g t gt--
(2)當前元素 i i 小於 v v 時, s w a p ( a r r [ i ] , a r r [ l t + 1 ] ) swap(arr[i], arr[lt+1]) l t + + lt++ i + + i++
(3)當前元素 i i 等於 v v 時, i + + i++
(4)最後排序的結果是 g t = i gt=i ,如下圖所示

在這裡插入圖片描述
(5)最後一步,將參考元素 v v 交換到合適的位置, s w a p ( a r r [ l ] , a r r [ l t ] ) swap(arr[l], arr[lt])

下面是C++程式實現:

#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

#include "MergeSorting.h"

using namespace std;

//對arr[l...r]進行partition操作
//
template<typename T>
int __partition2(T arr[], int l, int r){

    //隨機找一個元素與最左邊元素進行交換位置
    swap(arr[l], arr[rand()%(r-l+1)+l]);
    T v = arr[l];

    //arr[l+1...i)<=v; arr(j...r]>=v
    int i = l + 1;
    int j = r;
    while(true){
        while(arr[i] < v && i <= r) i++;
        while(arr[j] > v && j >= l + 1) j--;
        if (i > j) break;
        swap(arr[i], arr[j]);
        i ++;
        j --;
    }
    swap(arr[l], arr[j]);
    return j;
}

//對arr[l...r]部分進行排序
template<typename T>
void __quickSorting2(T arr[], int l, int r){
    if (l >= r)
        return;

    srand(time(NULL));
    int p = __partition2(arr, l, r);
    __quickSorting2(arr, l, p - 1);
    __quickSorting2(arr, p + 1, r);

}

template<typename T> //快速排序
void quickSorting2(T arr[], int n){

    __quickSorting2(arr, 0, n-1);

}

//////////////////////////////////////////////////
//////////////////////////////////////////////////


//對arr[l...r]部分進行排序
template<typename T>
void __quickSorting3ways(T arr[], int l, int r){
    if (l >= r)
        return;

    srand(time(NULL));
    //隨機找一個元素與最左邊元素進行交換位置
    swap(arr[l], arr[rand()%(r-l+1)+l]);
    T v = arr[l];

    //arr[l+1...lt]<v, arr[gt...r]>v, arr[lt+1...i-1]==v
    int lt = l, gt = r + 1, i = l + 1;
    while(i < gt){
        if (arr[i] > v){
            swap(arr[i], arr[gt-1]);
            gt--;
        }
        else if (arr[i] == v){
            i++;
        }
        else{
            swap(arr[i], arr[lt + 1]);
            lt++;
            i++;
        }
    }
    swap(arr[l], arr[lt]);

    __quickSorting3ways(arr, l, lt - 1);
    __quickSorting3ways(arr, gt, r);

}

template<typename T> //快速3路排序
void quickSorting3ways(T arr[], int n){

    __quickSorting3ways(arr, 0, n-1);

}

//int main(){
//    int arr[] = {8,7,6,5,4,3,2,1};
//    int n = 8;
//    quickSorting3ways(arr, n);
//    for(int i = 0; i < n; i++){
//        cout<<arr[i]<<" ";
//    }
//    cout<<endl;
//    return 0;
//}

int main()
{
    //生成5萬個隨機元素組成的陣列
    int n = 500000;
    int *arr = generateRandomArray(n, 0, n);
    int *arr2 = copyIntArray(arr, n);
    int *arr3 = copyIntArray(arr, n);
    cout<<"隨機陣列的排序比較"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("quickSorting3ways", quickSorting3ways, arr3, n);
    delete[] arr;//最後刪除陣列開闢的空間
    delete[] arr2;
    delete[] arr3;

    //生成5萬個近乎有序的陣列
    arr = generateNearlyOrderedArray(n, 100);//生成只有200個無序元素的陣列
    arr2 = copyIntArray(arr, n);
    arr3 = copyIntArray(arr, n);
    cout<<"近乎有序陣列的排序比較"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("quickSorting3ways", quickSorting3ways, arr3, n);
    delete[] arr;//最後刪除陣列開闢的空間
    delete[] arr2;
    delete[] arr3;

    //生成5萬個有大量重複元素的陣列
    arr = generateRandomArray(n, 0, 10);
    arr2 = copyIntArray(arr, n);
    arr3 = copyIntArray(arr, n);
    cout<<"有大量重複元素陣列的排序比較"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("quickSorting3ways", quickSorting3ways, arr3, n);
    delete[] arr;//最後刪除陣列開闢的空間
    delete[] arr2;
    delete[] arr3;

    return 0;
}

輸出結果為
在這裡插入圖片描述
可以看出對於隨機陣列或者近乎有序的陣列來說,三路快速排序要慢於快速排序,但是時間複雜度也是在可接受的範圍內;對於有大量重複元素的陣列來講,三路快速排序則要快於快速排序,因此三路快速排序的應用範圍是很廣泛的。