归并排序(MergeSort)
定义:
将数组分为两半,分别对这两半进行排序,然后将它们合并为一个有序的数组
该算法采用分治(divide and conquer)策略。分(divide)是将问题分成小块,治(conquer)是指攻克每个小块以达成解决方案。
具体:
1.将一个乱序的数组按mid值分为两个数组(新建一个数组用于存储顺序元素)。
2.将一个数组中的项与另一个数组中的项进行比较,将较小的项复制到新的第三个数组中。
3.当到达一个数组的末尾后,将另一个数组的剩余元素复制到新的第三个数组中。
如图:
import java.util.Arrays;
public class MergeSort {
public static <T extends Comparable<? super T>> void mergeSort(T[] a, int first, int last) {
// 先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
T[] tempArray = (T[]) new Comparable[a.length];
mergeSort(a, tempArray, first, last);
}
private static <T extends Comparable<? super T>> void mergeSort(T[] a, T[] tempArray, int first, int last) {
if (first >= last)
return;
else {
int mid = (first + last) / 2;
//左边归并排序,使得左子序列有序
mergeSort(a, tempArray, first, mid);
//右边归并排序,使得右子序列有序
mergeSort(a, tempArray, mid + 1, last);
//将两个有序子序列合并
merge(a, tempArray, first, mid, last);
}
}
private static <T extends Comparable<? super T>> void merge(T[] a, T[] tempArray, int first, int mid, int last) {
// 合并相邻子数组,a[first...mind]和a[mid+1...last]
int beginHalf1 = first;
int endHalf1 = mid;
int beginHalf2 = mid + 1;
int endHalf2 = last;
// tempArray中下一个可用的位置
int index = 0;
while ((beginHalf1 <=endHalf1) && (beginHalf2 <= endHalf2)) {
if (a[beginHalf1].compareTo(a[beginHalf2]) <=0) {
tempArray[index++] = a[beginHalf1++];
} else {
tempArray[index++] = a[beginHalf2++];
}
}
//将剩余项复制到tempArray中
while(beginHalf1 <=endHalf1) {
tempArray[index++]=a[beginHalf1++];
}
while(beginHalf2<beginHalf2) {
tempArray[index++]=a[beginHalf2++];
}
//将tempArray中的项复制到数组a中
for (int i = 0; i < index; i++)
a[first+i] = tempArray[i];
}
public static void main(String[] args) {
Integer[] a = { 45, 83, 72, 56, 95, 4, 3, 2,0,34,23,42,423, 1 };
MergeSort.mergeSort(a, 0, a.length-1);
System.out.println(Arrays.toString(a));
}
}
注意:
mergeSort将数组分为两半,然后递归的将每一半再分为两半,直到每一项只包含一项时为止。此时开始合并步骤,将一对含一项的子段合并为含有两项的子段,一对含两项的子段合并为含四项的子段,以此类推。用mergeSort与调用merge是交织在一起的,真正的排序是发生在合并步骤而不是发生在递归调用步骤。
归并排序的效率:
不管数组的初始状态如何,归并排序在最坏,最优,以及平均情形下都是O(nlogn)的。
唯一缺点:需要一个临时数组。