归并排序(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)的。
唯一缺点:需要一个临时数组。