归并排序是利用归并的思想实现排序,这里只介绍2路归并排序,其思想是:假设初始待排序队列记录数为n,看成n个长度为1的子序列,然后两两合并,得到个长度为2或1的有序子序列,再两两合并,……, 如此重复直至得到长度为n的有序序列为止。示意图如下。
实现2路归并排序需要额外的与待排序序列大小相同的空间。给出两个有序子序列合并的代码:
/**
* 两个有序序列的归并函数
*
* @Title: merge
* @Description: 将有序的array[begin,...mid]与
* array[mid+1,...,end]归并为assist[begin,...,end],最后复制到array
* @param array
* 待排序数组
* @param assist
* 辅助数据
* @param begin array [begin--------------mid--------------end]
* | | 有序 | 有序 |
* @param mid |
* |
* @param end assist [--------------------------------------]
* | 有序 |
*/
private void merge(int[] array, int[] assist, int begin, int mid, int end)
{
int i, j, k;
for (i = begin, j = mid + 1, k = begin; (i < mid + 1) && (j <= end); k++)
{
if (array[i] < array[j])
{
assist[k] = array[i++];
} else
{
assist[k] = array[j++];
}
}
if (i < mid + 1)
{
for (; i < mid + 1;)
{
assist[k++] = array[i++];
}
}
if (j <= end)
{
for (; j <= end;)
{
assist[k++] = array[j++];
}
}
// copy
System.arraycopy(assist, begin, array, begin, (end - begin + 1));
}
归并排序的递归实现:
/**
* 归并排序的实现
*
* @Title: mSort
* @Description: array借助于assist把下标从begin到end的序列进行归并排序
* @param array
* 待排序序列
* @param assist
* 辅助序列
* @param begin
* 起始下标
* @param end
* 结束下标
*/
private void mSort(int[] array, int[] assist, int begin, int end)
{
int mid;
if (begin == end)
{
return;
} else
{
mid = (begin + end) / 2;
mSort(array, assist, begin, mid);
mSort(array, assist, mid + 1, end);
merge(array, assist, begin, mid, end);
}
}
/**
* 归并排序的主函数
*
* @Title: mergeSort
* @Description: 给定待排序数组进行归并排序
* @param array
* 给定待排序数组
*/
public void mergeSort(int[] array)
{
// assist array
int[] assist = new int[array.length];
// sort
mSort(array, assist, 0, array.length - 1);
}
非递归实现,其核心思想就是如图所示,第一轮子序列长度为1进行归并,第二轮子序列长度为2进行归并,第三轮子序列长度为4进行归并,····,直至子序列长度大于待排序列的长度。每一轮归并后,剩余序列,分两种情况:1、剩余序列大于当前子序列,则表明剩余有两个序列,进行归并;2、剩余序列小于当前子序列(包括0),则不进行操作。代码如下:
/**
* 进行一趟归并排序
*
* @Title: mergePass
* @Description: 子序列长度为k,对array序列进行一趟归并操作。
* @param array
* 待排序序列
* @param assist
* 辅助序列
* @param k
* 子序列长度
*/
private void mergePass(int[] array, int[] assist, int k)
{
int index = 0;
while (index < array.length - 2 * k + 1)
{
merge(array, assist, index, (index + k - 1), (index + 2 * k - 1));
index += 2 * k;
}
if (index < array.length - k)
{
merge(array, assist, index, (index + k - 1), array.length - 1);
}
}
/**
* 归并排序非递归实现
*
* @Title: mergeSortNonRec
* @Description: 待排序序列array归并排序的非递归操作
* @param array
* 待排序序列
*/
public void mergeSortNonRec(int[] array)
{
// assist
int[] assist = new int[array.length];
// sort
int k = 1;
while (k < array.length)
{
mergePass(array, assist, k);
k *= 2;
}
}
性能分析:
归并排序是一种稳定的排序算法,每趟归并需要所有记录扫描一遍,耗时O(n)时间,而由完全二叉树深度可知,整个归并排序需要进行次,因此总的时间复杂度为
O(nlogn)。非递归实现时归并排序需要与待排序数组相同的存储空间,递归实现除了上述存储空间,还需要深度为的栈。而且归并排序平均情况、最好情况和最坏情况的时间复杂度都为
。
所以,归并排序是一种比较占用内存,但却效率高且稳定的排序算法。