归并排序的思想是,要是一个数组中的数字是两串已经排好序的,那把这两串儿数字并成一串数字,那这个数组就排好序了,所以又要先排好左右两边的子数组,这是一个明显的递归,直接看代码。AbstractSort在我的另一篇快排实现中有。
package sort;
public class MergeSort extends AbstractSort
{
Comparable[] aux;
@Override
public void sort(Comparable[] a)
{
// TODO Auto-generated method stub
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
public void sort2(Comparable[] a)
{
int len = a.length;
aux = new Comparable[len];
for (int sz = 1; sz < len; sz += sz)
{
for (int lo = 0,hi = 0,mid = 0; hi != len - 1; lo += sz + sz)
{
hi = lo + sz + sz - 1;
mid = lo + sz - 1;
if(hi >= len)
hi = len-1;
merge(a, lo, mid, hi);
}
}
}
/**
*
* @param a
* :传入想要归并的数组
* @param lo
* :想要归并的数组的最小索引
* @param hi
* :想要归并的数组的最大索引
*/
private void sort(Comparable[] a, int lo, int hi)
{
if (lo >= hi)
return;
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge(a, lo, mid, hi);
}
/**
*
* @param a
* : 传入想要归并的数组
* @param lo
* :左边那段数组的第一个索引
* @param mid
* :左边那段数组的最后一个索引
* @param hi
* :右边那段数组的最后一个索引
*/
public void merge(Comparable[] a, int lo, int mid, int hi)
{
int len = a.length;
aux = new Comparable[len];
for (int i = lo; i <= hi; i++)
{
aux[i] = a[i];
}
int i = lo;
int j = mid + 1;
for (int index = lo; index <= hi; index++)
{
if (i > mid)
a[index] = aux[j++];
else if (j > hi)
a[index] = aux[i++];
else if (less(aux[i], aux[j]))
a[index] = aux[i++];
else
a[index] = aux[j++];
}
}
}
sort2是归并排序的迭代实现,比较难以理解,思想是一样的。
“翻译”一下sort的实现:
1.找出数组的中间索引
2.将数组左半部分用归并排序排好
3.将数组右半部分用归并排序排好
3.将两个部分用merge方法归并成一个数组
第一行的if是递归的出口,当需要归并排序的子数组长度小于等于1时,结束递归过程。
虽然快排和归并的平均时间复杂度都是nlogn,但是我测试了一下,总是快排比较快,我猜想可能是归并排序的归并过程需要复制数字到另一个辅助数组,所以降低了速度,如果想要提高速度,可以不复制数字,而用指针来实现归并的过程,那速度应该会提高不少。但是代码就更加难理解了,不过思想还是一样的,有机会可以试试。