数据结构 第七章 排序——归并排序,基数排序

归并排序:
核心:有序子列的合并。
(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))。
稳定性:稳定。
关于基数排序的实现后面来补。

你可能感兴趣的:(数据结构与算法分析)