排序算法7---快速排序算法

原理:

通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

#include <stdio.h>
#define MAXSIZE 9

typedef struct {
    int r[MAXSIZE+1];    //r[0] 作为哨兵
    int length;            //待排序的数组长度
}SqList;

void print(SqList L){
    int i;
    for(i=1; i<MAXSIZE; i++){
    printf("%3d,",L.r[i]);
    }
    printf("%3d
",L.r[i]);
}

void swap(SqList * L, int i, int j){
    int temp=L->r[i];
    L->r[i]=L->r[j];
    L->r[j]=temp;
}

void QuickSort(SqList * L){
    void Qsort(SqList * L, int low, int high);
    Qsort(L,1,L->length);
}

void Qsort(SqList * L, int low, int high){
    int Partition(SqList * L,int low, int high);
    int pivot;
    //注意在边界的地方要判断,pivot=1或者pivot=length,做处理
    if(low < high){
    pivot=Partition(L, low, high);//将L->[low-high]一分为二,返回pivotkey所在位置的下标
    Qsort(L,low,pivot-1);
    Qsort(L,pivot+1,high);
    }
}

    int Partition(SqList * L,int low, int high){
        int pivotkey=L->r[low];
        while(low < high){
            while(low < high && L->r[high]>=pivotkey) high--;
            swap(L,low,high);

            while(low < high && L->r[low]<=pivotkey) low++;
            swap(L,low,high);
        }
        return low;    //返回pivotkey当前的下标
    }

int main(){
    SqList L;
    int num[MAXSIZE+1]={9,7,4,3,2,1,6,5,9,8};
    for(int i=0; i<10; i++){
        L.r[i] = num[i];
    }
    L.length=9;
//快排
    QuickSort(&L);
    print(L);
return 0;
}

时间复杂度

        快速排序涉及到递归调用,所以该算法的时间复杂度还需要从递归算法的复杂度开始说起;
       递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n)  ;参考之前归并排序对递归算法时间复杂度的计算;

最优情况下时间复杂度

        此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;
        下面来推算下,在最优的情况下快速排序时间复杂度的计算(用迭代法):
                                            T[n] =  2T[n/2] + n                                                                     ----------------第一次递归
                                                    =  2 { 2 T[n/4] + (n/2) }  + n                                               ----------------第二次递归

                                                    =  2^2 T[ n/ (2^2) ] + 2n

                                                    =  2^2  {  2 T[n/ (2^3) ]  + n/(2^2)}  +  2n                         ----------------第三次递归  

                                                    =  2^3 T[  n/ (2^3) ]  + 3n

                                                    =  2^m T[1]  + mn                                                  ----------------第m次递归(m次后结束),只需要对1个元素的情况进行复杂符分析,即T(1)。

               当最后平分的不能再平分时,也就是说把公式一直往下跌倒,到最后得到T[1]时,说明这个公式已经迭代完了(T[1]是常量了)。

               得到:T[n/ (2^m) ]  =  T[1]    ===>>   n = 2^m   ====>> m = logn;

               T[n] = 2^m T[1] + mn ;其中m = logn;

               T[n] = 2^(logn) T[1] + nlogn  =  n T[1] + nlogn  =  n + nlogn  ;其中n为元素个数

               又因为当n >=  2时:nlogn  >=  n  (也就是logn > 1),所以取后面的 nlogn,即最有情况下的时间复杂度为O(nlogn);(注意:这里logn表示以2为底的n的对数)

最差情况下时间复杂度                  这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

    平均时间复杂度    

       快速排序的平均时间复杂度也是:O(nlogn)。
 
平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n),那么:
 
用数学归纳法可证明,其数量级为0(nlogn).
 

空间复杂度

        其实这个空间复杂度不太好计算,因为有的人使用的是非就地排序,那样就不好计算了(因为有的人用到了辅助数组,所以这就要计算到你的元素个数了);我就分析下就地快速排序的空间复杂度吧;
        首先就地快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;
 
稳定性:
由于关键字的比较和交换是跳跃进行的,因此,快速排序是不稳定的排序方法。
原文地址:https://www.cnblogs.com/Allen-win/p/7307861.html