快速排序詳解(lomuto划分快排,hoare划分快排,classic經典快排,dualpivot雙軸快排源碼)


快速排序(lomuto划分快排,hoare划分快排,classic經典快排,dualpivot雙軸快排)

@

一、快速排序思想

快速排序的思想,是找出一個中軸(pivot),之后進行左右遞歸進行排序,關於遞歸快速排序,C程序算法如下。

void quick_sort(int *arr,int left,int right){
    if(left>right) return;
    int pivot=getPivot();
    quick_sort(arr,left,pivot-1);
    quick_sort(arr,pivot+1,right);
}

二、划分思想

關於划分,不同的划分決定快排的效率,下面以lomuto划分和hoare划分來進行講述思路

1.lomuto划分

思想:lomuto划分主要進行一重循環的遍歷,如果比left側小,則進行交換。然后繼續進行尋找中軸。最后交換偏移的數和最左側數,C程序代碼如下。

/**lomuto划分*/
int lomuto_partition(int *arr,int l,int r){
    int p=arr[l];
    int s=l;
    for(int i=l+1;i<=r;i++)
        if(arr[i]<p) {
            s++;
            int tmp=arr[i];
            arr[i]=arr[s];
            arr[s]=tmp;
        }
    int tmp=arr[l];
    arr[l]=arr[s];
    arr[s]=tmp;
    return s;
}
2.hoare划分

思想:hoare划分思想是先從右側向左進行尋找,再從左向右進行尋找,如果左邊比右邊大,則左右進行交換。外側還有一個嵌套循環,循環終止標志是一重遍歷,這種尋找的好處就是,在一次遍歷后能基本有序,減少遞歸的時候產生的比較次數。這也是經典快排中所使用的方法

/**hoare划分*/
int hoare_partition(int *a,int l, int r) {
    int p = a[l];
    int i = l-1;
    int j = r+1 ;
    while (1) {
        do {
            j--;
        }while(a[j]>p);
        do {
            i++;
        }while(a[i] < p);
        if (i < j) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }else
            return j;
    }
}
3.經典快排的划分改進

經典快排實際對hoare划分進行了少許改進,這個temp變量不需要每次找到左右不相等就立即交換,而是,暫時存放,先右邊向左找,將左邊放在右邊,再左邊向右找,把右邊放左邊,最后把初始temp變量放在左值。這樣比hoare划分減少少許移動變量次數。

/**經典快排*/
int classic_quick_sort(int *arr,int left,int right){
    int tmp=arr[left];
    while(left<right){
        while(left<right&&arr[right]>=tmp) right--;
        arr[left]=arr[right];
        while(left<right&&arr[left]<=tmp) left++;
        arr[right]=arr[left];
    }
    arr[left]=tmp;
    return left;
}
4.源碼補充(Java源碼)

在Java或者C#源碼中,Arrays.sort由多個排序算法構成,比如,數據量不大,使用雙軸快排(dualPivotQuickSort),數量巨大,使用歸並排序(merge sort),中間的先檢測下數據是否基本有序等特征,再使用相應的排序算法。

雙軸快排思想:總體思路是找出2個軸心。

我僅僅把選軸的部分進行挑出來進行將述,首先選定2個軸,L軸和R軸,使用i保存左值,j保存右值。首先從左向右邊找,如果比pivot1大,進行左值和偏移的自增,並且交換左值和偏移。如果在pivot1和pivot2之間,就直接繼續循環。循環中,如果比pivot大,那么從右往左邊找,如果值比pivot2大,那么進行跳出到OUT_LOOP的位置,如果在pivot1和pivot2之間,與pivot2交換,如果比pivot2小,交換j和左值,左值和右值。

dualPivot(int[] A,int L,int R){
    int pivot1 = A[L];
    int pivot2 = A[R];
    int i = L;
    int k = L+1;
    int j = R;
    OUT_LOOP:
    while(k < j){
        if(A[k] < pivot1){
            i++;
            Swap(A, i, k);
            k++;
        }else
        if(A[k] >= pivot1 && A[k] <= pivot2){
            k++;
        }else{
            while(A[--j] > pivot2){
                if(j <= k){
                    break OUT_LOOP;
                }
            }
            if(A[j] >= pivot1 && A[j] <= pivot2){
                Swap(A, k, j);
                k++;
            }else{
                i++;
                Swap(A, j, k);
                Swap(A, i, k);
                k++;
            }
        }
    }
    Swap(A, L, i);
    Swap(A, R, j);
    }
}

三、測試用例

關於測試,我使用C程序的<time.h>中的clock函數進行測試。測試代碼如下,數據量100'000:

int main()
{
    int data[100000];
    srand((int)time(0));
    for(int i=0;i<100000;i++){
        data[i]=rand();
    }
    clock_t start,end;
    start=clock();
    quick_sort(data,0,sizeof(data)/sizeof(int)-1);
    end=clock();
    printf("hore_partition %ld\n",(end-start));
    system("pause");
    return 0;
}

我們進行測試10次左右,發現結果如下圖所示:


結論:10w個數據進行排序,使用hore划分排序約14-15ms。使用lomuto划分排序約17~18ms左右,經典快排和lomuto的時間幾乎一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM