是创建在归并操作上的一种有效的排序算法,效率为 O(n\log n)}
归并算法流程
有两种不同的算法流程
1. 由顶到下(递归法)
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复上一步直到某一指针到达序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
2. 由低到上(迭代法)
- 将序列每相邻两个数字进行归并操作,形成ceil(n/2)个序列,排序后每个序列包含两/一个元素
- 若此时序列数不是1个则将上述序列再次归并,形成ceil(n/4)个序列,每个序列包含四/三个元素
- 重复上一步骤,直到所有元素排序完毕,即序列数为1
代码分析(递归法)
private static void merge(Comparable[] arr, int l, int mid, int r) {
Comparable[] aux = Arrays.copyOfRange(arr, l, r+1);
int i = l, j = mid+1;
for( int k = l ; k <= r; k ++ ){
if( i > mid ){
arr[k] = aux[j-l]; j ++;
}
else if( j > r ){
arr[k] = aux[i-l]; i ++;
}
else if( aux[i-l].compareTo(aux[j-l]) < 0 ){
arr[k] = aux[i-l]; i ++;
}
else{
arr[k] = aux[j-l]; j ++;
}
}
}
private static void sort(Comparable[] arr, int l, int r) {
if (l >= r)
return;
int mid = (l+r)/2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
对比归并排序和插入排序
对50000个随机数排序
由图可以看出我们的归并算法确实比插入排序算法使用时间更短,插入排序大概是他的100倍。
接着对50000个近似有序数组排序
>由图可以看出在近似有序的时候,插入排序还是更具优势。因为对近似有序的数组排序时,插入排序的复杂度可能会变为O(n),但是归并排序的复杂度则始终无法变为O(n)。
那么,能否提高归并算法的效率?
当然可以优化
private static void sort(Comparable[] arr, int l, int r) {
if (l >= r)
return;
int mid = (l+r)/2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
if (arr[mid].compareTo(arr[mid+1]) > 0) { //看这里 看这里
merge(arr, l, mid, r);
}
}
其实只要将merge方法增加一个判断就可以提高他的效率。如果说arr的第一组的最后一位数已经大于第二组的第一位,那我们就可以不对他进行merge。
可以看出确实为归并排序提高了一些效率。
其实还可以接着优化算法。
我们知道在近乎有序的数组中插入排序的算法确实有一些优势,而当数组越小时,数组近乎有序的概率越大。换句话说,当归并的到一定程度的时候其实就可以使用插入排序来替代归并排序。
private static void sort(Comparable[] arr, int l, int r) {
if (r-l <= 10){ // 看这里 看这里
new InsertionSort(arr,l,r);
return;
}
int mid = (l+r)/2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
if (arr[mid].compareTo(arr[mid+1]) > 0) {
merge(arr, l, mid, r);
}
}
可以看到,性能确实又有了一点提升。
代码分析(迭代法)
public MergeSortBU(Comparable[] arr) {
int n = arr.length;
for (int sz = 1; sz <= n; sz +=sz) {
for (int i = 0; i+sz <n ; i +=sz+sz) {
merge(arr,i,i+sz-1,(i+sz+sz-1)<n-1?(i+sz+sz-1):n-1);
}
}
}
迭代法其实就是先将数组分割成两个元素一组进行归并,接着再变为四个元素,八个元素等等。
直到全部归并结束。
MergeSortBU为从低向上,可以看出其实两种不同的方法的效率差不多。