经典排序算法(五) —— Merge Sort 归并排序
简介
约翰·冯·诺伊曼在 1945 年提出了归并排序。归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
归并排序的时间复杂度是O(nlogn)。代价是需要额外的内存空间。
排序过程
实现
版本一
@Test
public void test() {
int[] nums = new int[]{4, 2, 8, 9, 6, 3, 5, 7, 1, 2};
int[] mergeSort = mergeSortSimple(nums, 0, nums.length - 1);
printArray(mergeSort);
}
/*
* 归并排序
* @param nums 待排序数组
* @param start 开始位置
* @param end 结束位置
* @return
*/
public int[] mergeSortSimple(int[] nums, int start, int end) {
if (start == end) {
return new int[]{nums[start]};
}
int mid = (start + end) / 2;
int[] m1 = mergeSortSimple(nums, start, mid);
int[] m2 = mergeSortSimple(nums, mid + 1, end);
return mergeNumsSimple(m1, m2);
}
/**
* 合并两个有序数组
* @param num1 待合并数组1
* @param num2 待合并数组2
* @return 合并数组
*/
public int[] mergeNumsSimple(int[] num1, int[] num2) {
int l1 = num1.length;
int l2 = num2.length;
int[] resNum = new int[l1 + l2];
int resIdx = 0;
int index1 = 0;
int index2 = 0;
while (resIdx < resNum.length) {
while (index1 < l1 && index2 < l2) {
resNum[resIdx++] = num1[index1] < num2[index2] ? num1[index1++] : num2[index2++];
}
while (index1 < l1) {
resNum[resIdx++] = num1[index1++];
}
while (index2 < l2) {
resNum[resIdx++] = num2[index2++];
}
}
return resNum;
}
/**
* 打印数组
* @param nums
*/
public void printArray(int[] nums) {
for (int num : nums) {
System.out.print(num + "\t");
}
System.out.println();
}
版本二
@Test
public void test() {
int[] nums = new int[]{4, 2, 8, 9, 6, 3, 5, 7, 1, 2};
int[] res = new int[nums.length];
mergeSortSimpleOpt(nums, 0, nums.length - 1, res);
printArray(res);
}
/**
* 归并排序
*/
public void mergeSortSimpleOpt(int[] nums, int start, int end, int[] res) {
if (start == end) {
return;
}
int mid = (start + end) / 2;
mergeSortSimpleOpt(nums, start, mid, res);
mergeSortSimpleOpt(nums, mid + 1, end, res);
mergeOpt(nums, start, end, res);
}
/**
* 版本一递归过程中会有额外的临时数组开销,在此版本优化,直接传入定义好的结果数组做合并
*/
public void mergeOpt(int[] nums, int start, int end, int[] res) {
int end1 = (start + end) / 2;
int start2 = end1 + 1;
int idx1 = start;
int idx2 = start2;
int resIdx = idx1 + idx2 - start2;
while (idx1 <= end1 && idx2 <= end) {
res[resIdx++] = nums[idx1] < nums[idx2] ? nums[idx1++] : nums[idx2++];
}
while (idx1 <= end1) {
res[resIdx++] = nums[idx1++];
}
while (idx2 <= end) {
res[resIdx++] = nums[idx2++];
}
while (start <= end) {
nums[start] = res[start++];
}
}
复杂度
O(nlogn)