归并排序

本文深入讲解了归并排序算法的工作原理及其C语言实现过程。通过详细的步骤解释了如何将一个数组分割并逐步合并成有序数组,同时分析了归并排序的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引子 :高考分数线的制定,比如一本大学生全国共招生15万,那么如何排序这么多的学生,其实也就是每个市、每个县、每个学校、每个班级的排名合并后再排名得到的,最终得到一个总的高考参与学生的排名和分数,然后根据名额指定相应的分数线。

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

通俗理解:将数列从中间分割,直到只有一个数字的时候,合并(排序成有序数列),然后依次合并,直到合并后的数列长度与原始数列长度相等,形态类似于倒置的完全二叉树,如下图将{16,7,13,10,9,15,3,2,5,8,12,1,11,4,6}排序过程。


C语言代码如下:
/* 对顺序表 L 作归并排序 */
void MergeSort( SqList *L )
{
     MSort( L->r, L->r, 1, L->length );
}


/* 将SR[s...t]归并排序为TR1[s...t] */
void MSort( int SR[], int TR1[], int s, int t )
{
     /*
          s: start position of SR[]
          t: end positin of SR[]
          SR[]: the array to be adjusted
          TR1[]: the output array
     */
     int m,i;
     int TR2[10];
     if( s == t )     /* 递归出口,如果入点 s == 出点 t,那么说明 SR[] 中只有一个元素 */
          TR1[s] = SR[s];          /* 给TR[]中的元素赋值 */
     else
     {
          m = ( s + t )/2;     /* 将SR[s...t]平分(对半)为SR[s...m]和SR[m+1...t]*/
          MSort( SR, TR2, s, m );     /* 递归将SR[s...m]归并为有序的TR2[s...m] */
          MSort( SR, TR2,m+1, t );     /* 递归将SR[m+1...t]归并为有序的TR2[m+1...t] */
          Merge(TR2, TR1, s, m, t);     /* 将TR2[s...m]和TR2[m+1...t] 归并到TR1[s...t]*/
          for(; s <= t; s++)
               printf("%d\t",TR1[s]);
          printf("\n");
     }
     for( i = 1; i < 10 ; i++)
          printf("T: %d\t",TR2[i]);
     printf("\n");
}

/* 将有序的SR[i..m] 和 SR[m+1..n] 归并为有序的TR[i..n] */
void Merge( int SR[], int TR[], int i, int m, int n )
{
     int j, k, l;
     for( j = m+1, k = i; i <= m && j <= n; k++ )
     {
          /* 将SR中记录由小到大归并入TR */
          if( SR[i] < SR[j] )
               TR[k] = SR[i++];
          else
               TR[k] = SR[j++];
     }
 /* 两个if 最多有一个 满足if条件 */
     if( i <= m )
     {
          for( l = 0; l <= m-i; l++ )
               TR[k+l] = SR[i+l];          /* 将剩余的SR[i...m]复制到TR */
     }
     if( j <= n )
     {
          for( l = 0; l <= n-j; l++ )
               TR[k+l] = SR[j+l];          /* 将剩余的SR[j...n]复制到TR */
     }
}

MSort递归调用图解:


复杂度分析
  • 一趟归并需要将 SR[1]~SR[n] 中相邻的长度为h的有序序列进行两两归并。并将结果放到TR1[1]~TR[n]中,这需要将待排序列中的所有记录扫描一遍,因此消耗O(n)的诗句,而由完全二叉树的深度可知,整个归并排序需要进行log2 n次,因此总的时间复杂度为O(n logn),而且这是归并排序算法中最好、最坏、平均的时间性能。
  • 空间复杂度为 O(n + logn)
  • 因为Merge函数中有 if(SR[i] < SR[j])语句,两两比较,不存在跳跃,因此归并排序是一种稳定的排序算法。


注:非递归算法的效率要高于递归算法,在这里暂且先不学习了,以后追加。
非递归的迭代方法,避免了递归时深度为logn的栈空间,空间只是用到申请归并临时用的 TR 数组,因此空间复杂度减少O(n),并且避免递归也在时间性能上有一定的提升,应该说,使用归并排序,尽量考虑使用非递归方法。

ps.
typedef struct
{
	int r[MAXSIZE+1];	/* 用于存储要排序数组,r[0]用作哨兵或者临时变量 */
	int length;
} SqList;
/* 交换 L 中数组r的下标为 i 和 j 的值 */
void swap(SqList *L, int i, int j)
{
	int temp = L->r[i];
	L->r[i] = L->r[j];
	L->r[j] = temp;
}





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值