归并排序法是更高级的排序算法,核心思想:将一个数组一分为二,对两个子数组分别进行递归排序,最后将两个数组合并
递归实现归并排序
import java.util.Arrays;
public class Algorithm {
public static void main(String[] args) {
Integer[] arr = {6,6,3,2,4,7};
MergeSort.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
class MergeSort {
private MergeSort(){}
/**
* 用户只需要传入一个数组即可
*/
public static<E extends Comparable<E>> void sort(E[] arr){
sort(arr, 0, arr.length - 1);
}
/**
* 为了实现递归,必须要有拆分数组的边界索引,因此定义一个私有、对用户不可见的方法来进行递归
*/
private static<E extends Comparable<E>> void sort(E[] arr, int left, int right){
/**
* 如果只有一个或者没有元素,不需要进行操作
*/
if (left >= right){
return;
}
/**
* left + right有可能会产生整形溢出,因此写成如下方式可以避免出现这种错误
*/
int mid = left + (right - left) / 2;
sort(arr, left, mid);
sort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
/**
* 合并两个有序的区间arr[left, mid]和arr[mid + 1, right]
*/
public static<E extends Comparable<E>> void merge(E[] arr, int left, int mid, int right) {
/**
* left和mid + 1是两个分数组的开始索引
*/
int i = left;
int j = mid + 1;
/**
* 因为方法没有返回值,因此需要先保存一个原数组副本,然后将原数组按顺序覆盖
* 此处只需要保存[left, right + 1]范围内的数组而不是整个arr数组,否则空间复杂度会很大
*/
E[] tem = Arrays.copyOfRange(arr, left, right + 1);
for (int n = left; n < right + 1; n++) {
/**
* 如果左边的分数组排序完了,那就把右边的数组剩下的元素依次放进来
* 因为tem数组的索引是从0开始的,而我们要操作的arr数组的分数组是从left开始的,tem[0] == arr[left],i和j是由arr数组赋值的,所以跟tem有一个left的偏差
*/
if (i == mid + 1){
arr[n] = tem[j - left];
j++;
}
else if (j == right + 1) {
arr[n] = tem[i - left];
i++;
}
else if (tem[i - left].compareTo(tem[j - left]) <= 0) {
arr[n] = tem[i - left];
i++;
}
else{
arr[n] = tem[j - left];
j++;
}
}
}
}
添加辅助方法,打印递归的分步信息
import java.util.Arrays;
public class Algorithm {
public static void main(String[] args) {
Integer[] arr = {6,6,3,5,4,7};
MergeSort.sort(arr, 0);
System.out.println(Arrays.toString(arr));
}
}
class MergeSort {
private MergeSort(){}
public static<E extends Comparable<E>> void sort(E[] arr, int depth){
sort(arr, 0, arr.length - 1, depth);
}
private static<E extends Comparable<E>> void sort(E[] arr, int left, int right, int depth){
String depthScale = depth(depth);
System.out.print(depthScale);
System.out.println(String.format("当前要排序的数组是arr[%d, %d]", left, right));
if (left >= right){
return;
}
int mid = left + (right - left) / 2;
sort(arr, left, mid, depth + 1);
sort(arr, mid + 1, right, depth + 1);
System.out.print(depthScale);
System.out.println(String.format("当前排序完成的数组是arr[%d, %d]和arr[%d, %d]", left, mid, mid + 1, right));
merge(arr, left, mid, right, depth++);
System.out.print(depthScale);
System.out.println(String.format("当前合并完成的数组是arr[%d, %d]", left, right));
}
public static<E extends Comparable<E>> void merge(E[] arr, int left, int mid, int right, int depth) {
int i = left;
int j = mid + 1;
E[] tem = Arrays.copyOfRange(arr, left, right + 1);
for (int n = left; n < right + 1; n++) {
if (i == mid + 1){
arr[n] = tem[j - left];
j++;
}
else if (j == right + 1) {
arr[n] = tem[i - left];
i++;
}
else if (tem[i - left].compareTo(tem[j - left]) <= 0) {
arr[n] = tem[i - left];
i++;
}
else{
arr[n] = tem[j - left];
j++;
}
}
}
public static String depth(int depth){
StringBuilder str = new StringBuilder();
for (int i = 0; i < depth; i++) {
str.append("--");
}
return str.toString();
}
}
复杂度分析
从归并排序的递归树来看,递归深度为log2n,而每层所要处理的元素都是n个
因此归并排序的时间复杂度为O(nlogn)

选择、插入、归并排序性能比较
import java.util.Arrays;
import java.util.Random;
public class Algorithm {
public static void main(String[] args) {
Integer[] testScale = {10000, 100000};
for (Integer n : testScale) {
/**
* 测试随机数组排序性能
*/
Integer[] randomArr = ArrayGenerator.generatorRandomArray(n, n);
/**
* 测试有序数组排序性能
*/
Integer[] sortedArr = ArrayGenerator.generatorSortedArray(n, n);
/**
* Arrays.copyOf()方法复制数值的数值,为了测试更具有对比性
*/
Integer[] arr1 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr2 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr5 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr3 = Arrays.copyOf(sortedArr, sortedArr.length);
Integer[] arr4 = Arrays.copyOf(sortedArr, sortedArr.length);
Integer[] arr6 = Arrays.copyOf(sortedArr, sortedArr.length);
System.out.println("测试随机数组排序性能");
System.out.println();
Verify.testTime("SelectionSort", randomArr);
Verify.testTime("InsertionSort", arr1);
Verify.testTime("InsertionSortBetter", arr2);
Verify.testTime("MergeSort", arr5);
System.out.println();
System.out.println("测试有序数组排序性能");
System.out.println();
Verify.testTime("SelectionSort", sortedArr);
Verify.testTime("InsertionSort", arr3);
Verify.testTime("InsertionSortBetter", arr4);
Verify.testTime("MergeSort", arr6);
System.out.println();
}
}
}
class MergeSort {
private MergeSort(){}
public static<E extends Comparable<E>> void sort(E[] arr){
sort(arr, 0, arr.length - 1);
}
private static<E extends Comparable<E>> void sort(E[] arr, int left, int right){
if (left >= right){
return;
}
int mid = left + (right - left) / 2;
sort(arr, left, mid);
sort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
public static<E extends Comparable<E>> void merge(E[] arr, int left, int mid, int right) {
int i = left;
int j = mid + 1;
E[] tem = Arrays.copyOfRange(arr, left, right + 1);
for (int n = left; n < right + 1; n++) {
if (i == mid + 1){
arr[n] = tem[j - left];
j++;
}
else if (j == right + 1) {
arr[n] = tem[i - left];
i++;
}
else if (tem[i - left].compareTo(tem[j - left]) <= 0) {
arr[n] = tem[i - left];
i++;
}
else{
arr[n] = tem[j - left];
j++;
}
}
}
}
class InsertionSort {
private InsertionSort() {}
public static <E extends Comparable> void sort(E[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j--) {
if (arr[j].compareTo(arr[j - 1]) < 0) {
E tem;
tem = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tem;
}
else {
break;
}
}
}
}
}
class InsertionSortBetter {
private InsertionSortBetter() {}
public static <E extends Comparable> void sort(E[] arr) {
for (int i = 1; i < arr.length; i++) {
E tem = arr[i];
int j;
for (j = i; j > 0 && tem.compareTo(arr[j - 1]) < 0; j--) {
arr[j] = arr[j - 1];
}
arr[j] = tem;
}
}
}
class SelectionSort {
private SelectionSort(){}
public static<E extends Comparable<E>> void sort(E[] arr){
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j].compareTo(arr[minIndex]) < 0) {
minIndex = j;
}
}
if (i != minIndex) {
E tem;
tem = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tem;
}
}
}
}
class ArrayGenerator {
private ArrayGenerator (){}
public static Integer[] generatorRandomArray (Integer n, Integer maxBound){
Integer[] arr = new Integer[n];
Random random = new Random();
for (int i = 0; i < n; i++) {
arr[i] = random.nextInt(maxBound);
}
return arr;
}
public static Integer[] generatorSortedArray (Integer n, Integer maxBound){
Integer[] arr = new Integer[n];
for (int i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
}
class Verify {
private Verify (){}
public static<E extends Comparable<E>> boolean isSorted(E[] arr){
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i].compareTo(arr[i + 1]) > 0) {
return false;
}
}
return true;
}
public static<E extends Comparable<E>> void testTime(String AlgorithmName, E[] arr) {
long startTime = System.nanoTime();
if (AlgorithmName.equals("SelectionSort")){
SelectionSort.sort(arr);
}
if (AlgorithmName.equals("InsertionSort")) {
InsertionSort.sort(arr);
}
if (AlgorithmName.equals("InsertionSortBetter")) {
InsertionSortBetter.sort(arr);
}
if (AlgorithmName.equals("MergeSort")) {
MergeSort.sort(arr);
}
long endTime = System.nanoTime();
if (!Verify.isSorted(arr)){
throw new RuntimeException(AlgorithmName + "算法排序失败!");
}
System.out.println(String.format("%s算法,测试用例为%d,执行时间:%f秒", AlgorithmName, arr.length, (endTime - startTime) / 1000000000.0));
}
}
















