zoukankan      html  css  js  c++  java
  • 排序算法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)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;
     
    稳定性:
    由于关键字的比较和交换是跳跃进行的,因此,快速排序是不稳定的排序方法。
  • 相关阅读:
    一款纯css3实现的响应式导航
    一款基于TweenMax.js的网页幻灯片
    4款基于jquery的列表图标动画切换特效
    随着鼠标移动的文字阴影
    一款纯css实现的垂直时间线效果
    一款基于jquery的侧边栏导航
    (转) 共享个很棒的vim配置
    [Leetcode] String to Integer (atoi)
    dia无法输入中文?
    [Leetcode] Sum Root to Leaf Numbers
  • 原文地址:https://www.cnblogs.com/Allen-win/p/7307861.html
Copyright © 2011-2022 走看看