常见内部排序算法以及简要分析
插入排序
插入排序由N-1趟排序组成。对于P=1趟到P=N-1趟,插入排序保证从位置0到位置P上的元素是已排序状态。插入排序基于这样的事实:位置0到位置P-1上的元素是已经排过序的。
void InsertSort( int A[], int N ){ int i,j; for( i = 1; i < N; i++ ) { int tmp = A[i]; for( j = i; j >= 1 && A[j-1] > tmp; j--) A[j] = A[j-1]; A[j] = tmp; }}
插入排序的平均运行时间是O(N^2)。如果输入数据已经预先排序则运行时间为O(N).
堆排序
堆排序中利用二叉堆,建立N个元素的二叉堆需要花费O(N)的时间,然后执行N次DeleteMin操作。由于每个DeleteMin花费O(logN)时间,因此总的运行时间为O(NlogN).
基本想法是建立一个二叉堆,然后做N次DeleteMin操作
#define LeftChild( i ) (2 * ( i ) + 1 )static void Swap ( int *x,int *y ){ int tmp = *x; *x = *y; *y = tmp; }static void PercDown( int A[], int i, int N ){ int Child; int Tmp; for( Tmp = A[i]; LeftChild(i) < N; i = Child ) { Child = LeftChild(i); if( Child != N - 1 && A[Child + 1 ] > A[ Child ]) Child ++; if( Tmp < A[Child]) A[ i ] = A[Child]; else break; } A[i] = Tmp;}void HeapSort( int A[], int N ){ int i; for( i = N/2; i >= 0; i-- ) PercDown(A, i, N); for (i = N-1; i > 0; i--) { Swap(&A[0], &A[i]); PercDown(A, 0, i); }}
归并排序
归并排序以O(NlogN)最坏的运行时间运行,而且所使用的比较次数几乎是最优的。
算法的基本操作是:合并两个已排序的表。因为这两个表是已经排序的,所以若将输出放到第三个表中时则该算法可以通过对输入数据一趟排序来完成。
static void Merge( int A[], int TmpArray[], int Lpos,int Rpos,int RightEnd ){ int i, LeftEnd, NumInt, TmpPos; LeftEnd = Rpos-1; TmpPos = Lpos; NumInt = RightEnd-Lpos+1; while( Lpos <= LeftEnd && Rpos <= RightEnd ) { if( A[Lpos] <= A[Rpos]) TmpArray[TmpPos++] = A[Lpos++]; else TmpArray[TmpPos++] = A[Rpos++]; } while( Lpos <= LeftEnd ) TmpArray[TmpPos++] = A[Lpos++]; while( Rpos <= RightEnd ) TmpArray[TmpPos++] = A[Rpos++]; for(i = 0; i < NumInt; i++,RightEnd--) A[RightEnd] = TmpArray[RightEnd];}static void MSort( int A[], int TmpArray[], int Left, int Right ){ int Center; if( Left < Right ) { Center = (Left+Right)/2; MSort(A, TmpArray, Left, Center); MSort(A, TmpArray, Center+1, Right); Merge(A, TmpArray, Left, Center+1, Right); }}void MergeSort( int A[], int N ){ int *TmpArray; TmpArray = malloc(sizeof(int)*N); if( TmpArray != NULL) { MSort(A ,TmpArray, 0, N-1); free(TmpArray); } else printf("No Space of TmpArray!\n");}
虽然归并排序的运行时间是O(NlogN),但是它很难用于主存排序,主要问题在于合并两个排血的表需要线性附加内存,在整个算法中还需要花费将数据拷贝到临时数组再拷贝回来这样的操作中,其结果严重的放慢了排序的速度。然而,合并的例程是大多数外部排序算法的基石。
快速排序
快速排序是实践中最快的已知排序算法,平均运行时间是O(NlogN)。基本算法由以下简单的四步组成:
1.如果S中元素个数是0或者1,则返回。
2.取S中任一元素v,称之为枢纽元(pivot) 3.将S-{v}(S中的其余元素)分成两个不相交的集合:S1中包含的是小于v的元素,S2中包含的是大于v的元素 4.返回{quicksort(S1)}后,继而quicksort(S2)#define Cutoff (3)static void InsertionSort(int* NewStart ,int N){ }static void Swap ( int *x,int *y ){ int tmp = *x; *x = *y; *y = tmp; }static int Median3( int A[], int Left, int Right ){ int Center = (Left+Right)/2; if( A[Left] > A[Center]) Swap(&A[Left], &A[Center]); if( A[Left] > A[Right] ) Swap(&A[Left], &A[Right]); if( A[Center] > A[Right]) Swap(&A[Center], &A[Right]); Swap(&A[Center], &A[Right - 1]); return A[Right - 1];}static void Qsort( int A[], int Left, int Right ){ int i,j; int Pivot; if( Left + Cutoff <= Right ) { Pivot = Median3(A, Left, Right); i = Left; j = Right-1; 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); } }void QuickSort( int A[], int N ){ Qsort(A, 0, N-1);}