关于排序的一点思考

排序有很多种方法,最原始的方法莫过于插入排序。

插入排序

算法实现:

void InsertionSort(ElementType A[],int N){

    int i,j;

    ElementType Tmp;

    for ( i = 1;i < N ; i++){

        Tmp = A[i];

    for(j = i; j > 0 && Tmp < A[j-1]; j++)

        A[j] = A[j-1];

    A[j] = Tmp;

    }

}

时间复杂度分析:

插入排序是最简单粗暴的。插入排序的实现过程就像,假设有一篮球队选拔队员,让队员们从矮到高站好,插入排序的过程就像。让这一队先乱序站好,然后从第二个人开始(数组中下标从1开始),先让这个人出列(赋值给Tmp,这样做的目的是为了避免频繁的交换操作),让这个人和他前面的人比较,如果高过他,则把高的往后挪,直到碰到比他矮的(前面的以排好序,这里不考虑数值相同的情况)才结束,然后把这个人补到空位里面去(A[j]=Tmp)。很明显,这种排序很麻烦。

插入排序的时间复杂度显然是O(N2)。

希尔排序

算法实现:

void ShellSort(ElementType A[],int N){

    int i,j,Increment;

    ElementType Tmp;

    for(Increment = N/2;Increment > 0;Increment /=2)

         for(i = Increment; i < N;i++){

             Tmp = A[i];

            for(j = i;j > Increment && Tmp < A[j - Increment];j++)

                A[j] = A[j - Increment];

            A[j] = Increment;

        }

 }

希尔排序的评价:

个人感觉,希尔排序是一个很复杂的排序方法。

希尔排序的效率和增量序列(Increment)的选取有关。

 

堆排序:

堆排序是稳定的排序。(具体后面总结的时候和其他几种排序比较之后再提)

首先我们说的堆当然默认是二叉堆(binary heaps)。

为什么要使用堆排序?

因为够快和够稳定。理论上,堆排序的时间复杂度是O(NlogN),比希尔排序快(虽然实际的运用中希尔排序要更好一些)。这个时间是怎么来的?我们可以建一个堆,然后再将堆顶元素删除,将得到的元素记录在另一个数组中(当然这样会增加时间和空间的开销,不过这些开销并没有太多关系)。最后当堆中的元素都记录到另一个数组之后,整个排序就完成了,我们得到了一个递增的序列。另外的一个策略是(如果你不想使用一个增加开销的数组),我们把delete出来的数放在数组的最后一个位置,不断重复就可以得到一个有序序列。虽然对于小顶堆来说,这个序列是递减的。如果我们想要得到一个递增的序列的话,只需要调整一下heap order(变成大顶堆)再deleteMax就可以得到递增的序列了。

算法实现:

#define LeftChild(i) (2*(i)+1)

void PercDown(ElementType A[],ElementType i,int N){

     int Child;

    ElementType Tmp;

    for(Tmp = A[i];LeftChild(i) < N;i=Child){

    

    }

}

void HeapSort(ElementType A[],int N){

     /*build heap*/

     int i,j

     for(i=N/2;i>=0;i++)

     percdown(A,i,N);

    for(j=N-1;j>0;j--){

    swap(&A[0],&A[j]);

    perdown(A,0,i);

    }

}

    

 快速排序

快速排序正如他的名字所示的那样,理论上是最快。

 算法实现:

快速排序很重要的一点是pivot的选取。这里采用的方法是三数中值法,去头尾和中间三个元素,然后取三个数的中值作为pivot的值。(下面对快排做具体分析的时候会解释pivot的选取和时间复杂度的问题)

#define Cutoff 3

/*Median3用来选取pivot 并且返回pivot*/

ElementType Median3(ElementType A[], int Left, int Right){

    int Center;

    Center = (Left + Right)/2;

 

    if(A[Left] > A[Right])

         Swap(&A[Left],&A[Right]);

    if(A[Left] > A[Center])

         Swap(&A[Left],&A[Center]);

    if(A[Center] > A[Right])

         Swap(&A[Center],&A[Right]);

    Swap(&A[Center],&A[Right-1]);

    return A[Right-1];

}

    

void QSort(ElementType A[], int Left, int Right){

     //对于元素较少的子数组来说,不能用三数中值法来选取枢纽元素,不能用快速排序来实现,可选用插入排序。

     if(Left + Cutoff <= Right){

         ElementType pivot;

         int i,j;

         i = Left;

         j = Right;

         for( ; ; ){

             while(A[++i] < pivot){}

             while(A[-- j] > pivot){}

             if(i < j)

                 Swap(&A[i], &A[j]

             else break;

         } 

        Swap(&[A[i],&A[Right-1]);

        QSort(A,Left,i-1);

        QSort(A,i + 1,Right);

    }

    else 

        InsertionSort(A+Left, Right-Left+1);

}

 

 

 

 

 

 

 

 

               

posted @ 2017-03-10 15:21  玫瑰色的你  阅读(187)  评论(0编辑  收藏  举报