数据排序(二)
归并排序
- 归并排序(MERGE SORT)是又一类不同的排序方法,归并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。
- 例如,有两个有序表,(7,10,13,15)和(4,8,19,20),归并后得到的有序表为(4,7,8,10,13,15,19,20) 。
- 归并过程:比较a[i]和b[j]的大小,若a[i]<=b[j],则将第一个有序序列中的元素a[i]复制到r[k]中,并令i和k分别加1,分别指向后一个单元,否则将第二个有序序列的元素b[j]复制到r[k]中,并令j和k分别加1;如此循环下去,直到其中一个有序表取完,然后将另一个有序表的剩余元素复制到r中.当然,a数组和b数组在实际应用中往往是同一个数组分成前后两部分。算法如下:
//二路归并算法描述为(a[s,t]中的数据由小到大合并到r[s,t]中)
//两个有序表a[s,m]和a[m+1,t]合并成一个有序表r[s,t]
//以下是算法的伪代码
1 procedur merge(s,m,t); 2 begin 3 (1) i:=s; j:=m+1; k:=s; 4 (2) while (i<=m) and (j<=t) do 5 if a[i]<=a[j] then [r[k]:=a[i];inc(i);inc(k)] 6 else [r[k]:=a[j];inc(j);inc(k)]; 7 (3) while i<=m do [r[k]:=a[i];inc(i);inc(k)]; 8 (4) while j<=t do [r[k]:=a[j];inc(j);inc(k)]; 9 end;
- 归并排序的基本思想就是假设数组A有N个元素,那么可以看成数组A是有N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个 N/2 个长度为2或1的有序子序列,再两两合并,如此重复,直到得到一个长度为N的有序数据序列为止,这种排序方法称为2路归并排序。
- 归并排序算法我们用递归实现,先把待排序区间[s,t]以中点二分,接着对左右两部分分别归并排序,然后把排序后的两个序列合并成有序区间[s,t]。其中,对子序列的排序与原问题一样,所以可以调用相同的子程序,只是区间不一样。
1 procedure mergesort(s,t:longint); //对数组a的s到t的区间进行排序,r为临时数组 2 var 3 m,i,j,k:longint; 4 begin 5 if s=t then exit; //如果区间只有1个数据就不用排了 6 m:=(s+t) div 2; //取区间中点 7 mergesort(s,m); //以中点二分,分别排序 8 mergesort(m+1,t); 9 i:=s; 10 j:=m+1; 11 k:=s; 12 while (i<=m) and (j<=t) do //合并两个子序列,直到其中一个取完数 13 if a[i]<=a[j] then 14 begin r[k]:=a[i];inc(i);inc(k);end 15 else 16 begin r[k]:=a[j];inc(j);inc(k);end; 17 while i<=m do begin r[k]:=a[i];inc(i);inc(k);end; //如果有剩余,直接复制到r 18 while j<=t do begin r[k]:=a[j];inc(j);inc(k);end; 19 for i:=s to t do a[i]:=r[i]; //合并后的数据放回a数组 20 end;
- 效率分析:归并排序的的时间复杂度为O(n*log2n),排序规模可达万、十万级别,而且还是一种稳定的排序,缺点是,需要设置一个临时数组,空间占用稍大,而且也是基于递归结构的,容易受系统栈限制,如果改成非递归结构,编程复杂度增加。
- 算法改进:自然合并排序是合并排序算法的一种改进. 自然合并排序:对于初始给定的数组,通常存在多个长度大于1的已自然排好序的子数组段.例如, 若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}.用一次对数组a的线性扫描就足以找出所有这些排好序的子数组段.然后将相邻的排好序的子数组段两两合并,构成更大的排好序的子数组段({3,4,7,8}, {1,2,5,6}).继续合并相邻排好序的子数组段,直至整个数组已排好序.
各种排序算法的比较
- 稳定排序:
冒泡排序
插入排序
二叉树排序
归并排序
- 不稳定排序:
选择排序
快速排序
堆排序
- 时间复杂O(n2)级别:
冒泡排序
选择排序
插入排序
- 时间复杂度O(n*log2n)级别:
快速排序
归并排序
堆排序