首页 > 代码库 > QuickSort快速排序的多种实现

QuickSort快速排序的多种实现

并不是很懂wikipedia上面说快排的空间复杂度最坏情况是O(NlogN)啊,难道不是空间复杂度平均O(logN),最坏O(N)么……原地快排难道不是只要算递归栈深度就好了么……有谁给我解释一下啊(评论区orz)。

快排的核心思想我感觉就是选一个pivot,然后将待排序数列通过与pivot比较分为两个部分A和B(A部分的数均小于或等于pivot,B部分均大于或等于pivot),这样划分好的数列中的数就处于A <= pivot <= B的状态,接着对划分好的两个部分A和B分别递归调用快排函数,最后一定能出现划分好的部分中只剩下一个元素或没有元素的情况,而对于只有一个元素的数列或空列,我们认为它已经是有序的,这样就把数列排好了。

所以在快排中怎样对数组进行划分是关键,不同版本的快速排序大体结构都差不多,不同之处就在于划分函数的不同,这里就暂时称它为Partition函数吧,下面介绍四种不同的实现。

第一个版本

其实快排一开始不是我们现在看到的这样的,一开始Hoare发明的版本在《算法导论》这本书的思考题7-1上有提及。这个版本相当于是以开头元素为pivot,然后两个index分别从头和尾开始遍历,暂时把这两个index变量称为i和j吧,i从头开始,j从尾开始,其过程如下:

第一步——j往前遍历,遇到小于或等于pivot的数就停止

第二步——i往后遍历,遇到大于或等于pivot的数就停止

第三步——比较i和j大小,j > i则交换i和j位置上的数并在此基础上继续第一步,否则函数返回j

函数返回后,经过Partition函数的调用,数组中下标为j之前(包括j)的元素都小于或等于pivot,j之后的元素都大于或等于pivot,然后对这两部分进行快速排序。

例子:

技术分享

代码如下:

int Hoare_Partition(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin - 1;;
    int j = end + 1;
    int tmp;
    
    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            return j;
        }
    }
}

void Quick_Sort_1(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = Hoare_Partition(A, begin, end);
    Quick_Sort_1(A, begin, pivot_position);
    Quick_Sort_1(A, pivot_position + 1, end);
}

第二个版本

这个版本是对Hoare原版本的一个改进,Hoare原版本中Partition函数调用结束后pivot会存在于划分好的两个部分中的某一个中,因此第二个版本对此进行了改进,同样选取第一个元素作为pivot,如果数列的下标从begin开始,end结束的话,那么用Hoare的处理思想去处理从begin + 1到end部分的数列(Hoare的版本是处理从begin到end部分的数列的),这样处理完成后下标为begin + 1到j的数都是小于或等于pivot的,而下标为j + 1到end的数都是大于或等于pivot的,最后再将pivot和下标为j的数进行交换并返回j,这样一来下标为j之前(不包括j)的数均小于或等于pivot,下标为j之后的数均大于或等于pivot,pivot也就不在两个划分好的部分中,而独立存在了,因此只要对两个部分继续快排就好了,而不需要再将pivot包括进去。

例子:技术分享

代码如下:

int New_Partition_1(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;
    int tmp;
    
    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            A[begin] = A[j];
            A[j] = pivot;
            return j;
        }
    }
}

void Quick_Sort_2(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_1(A, begin, end);
    Quick_Sort_2(A, begin, pivot_position - 1);
    Quick_Sort_2(A, pivot_position + 1, end);
}

 第三个版本

这个版本是现在比较常见的版本之一,将pivot所在位置挖成一个坑,i从begin + 1开始,j从end开始,然后:

第一步——j往前遍历,找到小于或等于pivot的数

     如果此时j > i,将下标为j的数填入现在的坑中,并在下标为j的位置产生一个新坑,进行第二步

     否则就将pivot填入现在的坑中,并返回j

第二步——i往后遍历,找到大于或等于pivot的数

     如果此时j > i,将下标为i的数填入现在的坑中,并在下标为i的位置产生一个新坑,进行第一步

     否则就将pivot填入现在的坑中,并返回i

例子:

技术分享

代码如下:

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;
    
    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_3(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_2(A, begin, end);
    Quick_Sort_3(A, begin, pivot_position - 1);
    Quick_Sort_3(A, pivot_position + 1, end);
}

第四个版本

这个版本也很常见,就是把最后一个数作为pivot,i一开始等于begin,用j从begin开始往end - 1遍历,找到小于或等于pivot的数,与i所在位置的数交换,并将i++,最后将pivot和下标为i的数交换,返回i。

例子:

技术分享

代码如下:

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;
    
    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_4(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_3(A, begin, end);
    Quick_Sort_4(A, begin, pivot_position - 1);
    Quick_Sort_4(A, pivot_position + 1, end);
}

 完整代码:

//
//  main.c
//  Quick Sort
//
//  Created by 余南龙 on 2016/11/29.
//  Copyright ? 2016年 余南龙. All rights reserved.
//

#include <stdio.h>

int Hoare_Partition(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin - 1;;
    int j = end + 1;
    int tmp;
    
    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            return j;
        }
    }
}

int New_Partition_1(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;
    int tmp;
    
    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            A[begin] = A[j];
            A[j] = pivot;
            return j;
        }
    }
}

int New_Partition_2(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;
    
    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        if(j > i){
            A[i] = A[j];
        }
        else{
            A[i] = pivot;
            return i;
        }
        do{
            i++;
        }while(A[i] < pivot&&i < j);
        if(j > i){
            A[j] = A[i];
        }
        else{
            A[j] = pivot;
            return j;
        }
    }
}

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;
    
    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_1(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = Hoare_Partition(A, begin, end);
    Quick_Sort_1(A, begin, pivot_position);
    Quick_Sort_1(A, pivot_position + 1, end);
}

void Quick_Sort_2(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_1(A, begin, end);
    Quick_Sort_2(A, begin, pivot_position - 1);
    Quick_Sort_2(A, pivot_position + 1, end);
}

void Quick_Sort_3(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_2(A, begin, end);
    Quick_Sort_3(A, begin, pivot_position - 1);
    Quick_Sort_3(A, pivot_position + 1, end);
}

void Quick_Sort_4(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_3(A, begin, end);
    Quick_Sort_4(A, begin, pivot_position - 1);
    Quick_Sort_4(A, pivot_position + 1, end);
}

int main(int argc, const char * argv[]) {
    int A_1[10] = {2, 4, 6, 1, 5, 9, 0, 8, 7, 3};
    int A_2[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int A_3[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    int A_4[10] = {3, 9, 1, 7, 0, 2, 8, 5, 4, 6};
    int i;
    
    Quick_Sort_1(A_1, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_1[i]);
    }
    putchar(\n);
    Quick_Sort_2(A_2, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_2[i]);
    }
    putchar(\n);
    Quick_Sort_3(A_3, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_3[i]);
    }
    putchar(\n);
    Quick_Sort_4(A_4, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_4[i]);
    }
    putchar(\n);
}

 

QuickSort快速排序的多种实现