归并排序是一种经典的排序算法,需要掌握(毕竟是冯诺依曼在80年前就发明的一种经典算法),它把排序的时间复杂度降到O(nLogn)。它体现了分治的思想,这种思想深深地渗透在计算机科学的各个领域里面。
先生成一个对数器,来测试我们写的排序算法是否正确。
package com.example.demo.algorithm.sort;
import java.util.Arrays;
import java.util.Random;
/**
* @author lxw
* @date 2022/5/26 9:09
*/
public class RandomCheckUtils {
/**
* jdk自带的排序
*
* @param arr
*/
public static void jdkComparator(int[] arr) {
Arrays.sort(arr);
}
/**
* 生成一个随机数组,大小为maxSize,数组每个元素值的范围为[0,maxValue]
*
* @param maxSize
* @param maxValue
* @return
*/
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[maxSize];
for (int i = 0; i < arr.length; i++) {
arr[i] = new Random().nextInt(maxValue + 1);
}
return arr;
}
/**
* 检验两个数组的所有元素是否相等
*
* @param arr
* @param targetArr
* @return
*/
public static boolean isEquals(int[] arr, int[] targetArr) {
// 两个都为null
if (null == arr && null == targetArr) {
return true;
}
// 其中一个为null
if ((null == arr && null != targetArr) || (null != arr && null == targetArr)) {
return false;
}
//两个长度不相等
if (arr.length != targetArr.length) {
return false;
}
// 两个长度相等
for (int i = 0; i < arr.length; i++) {
if (arr[i] != targetArr[i]) {
return false;
}
}
return true;
}
/**
* 拷贝原数组,生成新的数组
*
* @param arr
* @return
*/
public static int[] copyArray(int[] arr) {
int[] newArray = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArray[i] = arr[i];
}
return newArray;
}
}
再写一个归并算法,用对数器去校验。
package com.example.demo.algorithm.sort;
/**
* @author lxw
* @date 2022/5/26 9:07
*/
public class MergeSort {
/**
* 入参为数组,里面的逻辑开始执行归并排序
*
* @param arr
*/
public static void mergeSortProcess(int[] arr) {
int size = arr.length;
int start = 0;
int end = size - 1;
mergeArr(arr, start, end);
}
/**
* 在arr的[start,end]上归并排序好
*
* @param arr
* @param start
* @param end
*/
public static void mergeArr(int[] arr, int start, int end) {
// 如果start=end,则已经排序好
if (start == end) {
return;
}
// 获取中点mid
int mid = start + (end - start) / 2;
// 在arr的[start,mid]上归并排序好
mergeArr(arr, start, mid);
// 在arr的[mid+1,end]上归并排序好
mergeArr(arr, mid + 1, end);
// 在arr的[start,mid]和[mid+1,end]上进行merge操作
merge(arr, start, mid, end);
}
/**
* 在arr的[start,mid]和[mid+1,end]上进行merge操作
*
* @param arr
* @param start
* @param mid
* @param end
*/
public static void merge(int[] arr, int start, int mid, int end) {
// 开辟一个新的数组
int[] newArr = new int[end - start + 1];
// 定义新数组的下标
int newArrIndex = 0;
// 定义[start,mid]的起始下标
int pointLeft = start;
// 定义[mid+1,end]的起始下标
int pointRight = mid + 1;
// 在pointLeft和pointRight都没越界的情况下进行merge
while (pointLeft <= mid && pointRight <= end) {
newArr[newArrIndex++] = arr[pointLeft] <= arr[pointRight] ? arr[pointLeft++] : arr[pointRight++];
}
// pointRight先越界
while (pointLeft <= mid) {
newArr[newArrIndex++] = arr[pointLeft++];
}
// pointLeft先越界
while (pointRight <= end) {
newArr[newArrIndex++] = arr[pointRight++];
}
// 把新的数组赋值给原来的数组
for (int i = 0; i < newArr.length; i++) {
arr[start + i] = newArr[i];
}
}
public static void main(String[] args) {
int maxSize = 10;
int maxValue = 1000;
// 随机生成一个数组
int[] arr = RandomCheckUtils.generateRandomArray(maxSize, maxValue);
// 生成对比数组
int[] arr2 = RandomCheckUtils.copyArray(arr);
// 自己的排序
mergeSortProcess(arr);
// jdk的排序
RandomCheckUtils.jdkComparator(arr2);
if (RandomCheckUtils.isEquals(arr, arr2)) {
System.out.println("success!");
} else {
System.out.println("fail!");
}
}
}
思考:
为啥归并排序相比于选择,冒泡和插入排序的时间复杂度低?
因为在每一次递归排序完merge的过程把排序信息记录了下来,传递了下去。