归并排序:
核心:有序子列的合并。
(1)将两个有序子列(存放在同一顺序表中的相邻位置)归并成一个有序数列的算法:
void Merge( ElementType a[], ElementType TmpA[], int L, int R, int RightEnd )
{
int LeftEnd = R - 1;
int tmp = L;
int Element_number = RightEnd - L + 1;
while( L <= LeftEnd && R <= RightEnd ){
if( a[L] < a[R] )
TmpA[tmp++] = a[L++];
else
TmpA[tmp++] = a[R++];
}
while( L <= LeftEnd ){
TmpA[tmp++] = a[L++];
}
while( R <= RightEnd ){
TmpA[tmp++] = a[R++];
}
for( int i = 0; i < Element_number; i++, RightEnd-- )
a[RightEnd] = TmpA[RightEnd];
}
(2)归并排序递归算法(基于分治):
void MSort( ElementType a[], ElementType TmpA[], int L, int RightEnd )
{
int center; //分界点
if( L < RightEnd ){ //
center = ( L + RightEnd ) / 2; //找到分界点
MSort( a, TmpA, L, center ); //把分界点左边递归的进行归并排序
MSort( a, TmpA, center + 1, RightEnd ); //把分界点右边递归的进行归并排序
Merge( a, TmpA, L, center + 1, RightEnd ); //把分界点两边有序的子列归并成一个数列
}
}
修改成统一接口后:
void Merge_Sort( ElementType a[], int n )
{
ElementType *Tmp;
Tmp = ( ElementType* )malloc( n * sizeof( ElementType ) ); //申请临时空间
if( Tmp != NULL ){
MSort( a, Tmp, 0, n - 1 ); //调用归并排序递归算法
free( Tmp ); //释放所申请空间
}
else
printf( "空间不足\n" );
}
空间复杂度:申请了一个大小为n的辅助空间,所以空间复杂度为O(n)。
时间复杂度:O(nlogn)(每一趟归并(即Merge函数所做的事情)排序时间复杂度为O(n),共进行logn向上取整(此处存在疑惑)次归并)。
稳定性:稳定。
(3)非递归算法:
void Merge_1( ElementType a[], ElementType TmpA[], int L, int R, int RightEnd )
{
int LeftEnd = R - 1;
int tmp = L;
int Element_number = RightEnd - L + 1;
while( L <= LeftEnd && R <= RightEnd ){
if( a[L] < a[R] )
TmpA[tmp++] = a[L++];
else
TmpA[tmp++] = a[R++];
}
while( L <= LeftEnd ){
TmpA[tmp++] = a[L++];
}
while( R <= RightEnd ){
TmpA[tmp++] = a[R++];
}
}
void Merge_Pass( ElementType a[], ElementType TmpA[], int n, int length )
{
//length是当前有序子列的长度,初始值为1
//由于n可能是奇数也可能是偶数,所以归并的时候最后两个(偶数情况)或最后一个(奇数情况)单独拿出来讨论
int i, j;
for( i = 0; i <= n - 2 * length; i += length * 2 ) //2-路归并,相邻两个元素进行归并排序
Merge_1( a, TmpA, i, i + length, i + 2 * length - 1 );
if( i + length < n ) //说明还剩两个子列没有归并
Merge_1( a, TmpA, i, i + length, n - 1 );
else //说明只剩一个子列,此时把这个子列接在数组后面
for( j = i; j < n; j++ )
TmpA[j] = a[j];
}
void Merge_Sort_( ElementType a[], int n )
{
int length = 1; //定义初始归并子列的长度为1
ElementType *Tmp;
Tmp = ( ElementType *)malloc( n * sizeof( ElementType ) );
if( Tmp != NULL ){
while( length < n ){
Merge_Pass( a, Tmp, n, length ); //一趟归并把排好序的数列存在数组Tmp中
length *= 2; //有序子列的长度加倍
Merge_Pass( Tmp, a, n, length ); //第二趟归并,且把排好序的数列倒回数组a中
length *= 2; //有序数列的长度加倍
}
free( Tmp );
}
else
printf( "空间不足\n" );
}
基数排序(桶排序):不基于比较进行排序,采用多关键字排序思想。
主位优先排序(MSD,Most Significant Digit)(最高位优先)。
次位优先排序(LSD, Least Significant Digit)(最低位优先)。
步骤分为分配和收集。
空间效率:一趟排序需要的辅助空间为O( N + B )(其中n是元素的个数,B是基数,也就是桶的个数)。
时间效率:基数排序需要P趟,每一趟分配需要遍历所有元素O(n),收集需要遍历所有桶O(B),所以时间复杂度为O( P(N+B))。
稳定性:稳定。
关于基数排序的实现后面来补。