归并排序看起来比较难,代码量也比之前几个排序多,研究归并排序也确实用了挺长时间。
归并排序中的“归”是递归的意思,“并”是合并的意思,顾名思义,归并就是先递归分开,再合起来,那么归并排序的思想就是将一个数据序列递归划分为越来越小的半子表,再对半子表进行排序,最后再用递归的方法将排好序的半子表合并成为越来越大的有序序列,这也是分治思想在归并排序中的体现。
归并排序算法的原理就是对于给定的一组记录(假设一共有n个记录),先将其以每两个相邻的长度为1的子序列进行划分,得到n/2(向上取整)个长度为2或1的有序子序列,再将其两两合并,反复执行此过程,直到得到一个有序序列。
所以归并排序的主要思想就是先分后合,以数组{36,21,25,42,38,27,16}举例说明:
初始关键字:{36,21} {25,42} {38,27} {16}
一趟归并后:{21,36} {25,42} {27,38} {16}
二趟归并后:{21,25,36,42} {16,27,38}
三趟归并后:{16,21,25,27,38,42}
示例代码及结果如下:
package pp.suanfa;
/**
* 归并排序
*
* @author xiaoGd
*
*/
public class MergeSort {
public static void Merge(int array[],int p,int q,int r)
{
int i,j,k,n1,n2;
n1 = q-p+1;
n2 = r-q;
//一次将数组拆分为两个半子表
int[] L = new int[n1];
int[] R = new int[n2];
for(i=0,k=p;i<n1;i++,k++)
{
L[i] = array[k];
}
for(i=0,k=q+1;i<n2;i++,k++)
{
R[i] = array[k];
}
//将两个半子表进行排序放回到数组里
for(i=0,j=0,k=p;i<n1&&j<n2;k++)
{
if(L[i]>R[j])
{
array[k] = L[i];
i++;
}
else
{
array[k] = R[j];
j++;
}
}
//若数组长度为奇数,则将剩余的半子表放回到数组里
if(i<n1)
{
for(j=i;j<n1;j++,k++)
{
array[k] = L[j];
}
}
if(j<n2)
{
for(i=j;i<n2;i++,k++)
{
array[k] = R[i];
}
}
}
//递归实现拆分和重组
public static void MergeSort(int array[],int p,int r)
{
if(p<r)
{
int q = (p+r)/2;
MergeSort(array,p,q);
MergeSort(array,q+1,r);
Merge(array,p,q,r);
}
}
//测试
public static void main(String[] args) {
int i = 0;
int[] a = {5,6,4,7,9,1,3,8,2};
int len = a.length;
MergeSort(a,0,len-1);
for(i=0;i<len;i++)
{
System.out.print(a[i]+" ");
}
}
}
由于归并每次都是折半,所以一共需要进行logn趟。每一趟归并排序的操作,就是将两个有序子序列进行归并,而每一对有序子序列归并时,记录的比较次数均小于记录的移动次数,记录移动的次数均等于文件中记录的个数n,即每一趟归并排序的时间复杂度为O(n)。因此,二路归并排序的时间复杂度为O(nlogn)。