归并排序
原理
- 将一个无序数据拆分成两个,然后分别对这两个子数组进行排序,子数组排序完成后,将数组合并为一个数据
- 递归上述动作
这里仅仅表述了归并排序的基本原理,并非真的每次都要申请新的内存空间在存储拆分开的两个数组,其实如果每次都取中间位置进行拆分,通过递归,最终会拆分成两个最小子数组,每个数组都只有一个元素
其实并不是真的拆分成连个数组,而是获取了原数组中的下标范围,来标识两个数组
实现
public class MergeSortTest {
@Test
public void testMergeSort() {
int[] sourceArr = {4, 1, 6, 8, 9, 3, 5, 6, 8, 3, 2, 10, 5, 14, 34, 32, 23};
mergeSort(sourceArr, 0, sourceArr.length - 1);
}
private void mergeSort(int[] sourceArr, int low, int high) {
//递归终止条件
if (low >= high) {
return;
}
//取中间位置进行分治
int middle = (low + high) / 2;
//递归分治左侧数组
mergeSort(sourceArr, low, middle);
//递归分治右侧数组
mergeSort(sourceArr, middle + 1, high);
//归并操作
merge(sourceArr, low, middle, high);
System.out.println(Arrays.toString(sourceArr));
}
private void merge(int[] sourceArr, int low, int middle, int high) {
//临时数组,两个数组排序后,临时存储在这里
int[] temp = new int[sourceArr.length];
//左侧数组起始位置
int i = low;
//右侧数组起始位置
int j = middle + 1;
//临时数组起始位置
int k = 0;
//1.对两个数组进行排序,并存入临时数组中,
//2.临时数组当前位置一定存储两个数组相同位置较小的元素,决定了排序方向为从小到大,
//3.如果两个数组数量不相同,元素较多的数组会有元素剩下,但是递归的最内层,两个数组都是只有一个元素,可以排序后归并为一个含有两个元素的数组
while (i <= middle && j <= high) {
if (sourceArr[i] <= sourceArr[j]) {
//这里 <= ,如果两个元素相等,会优先取左侧数组中的元素,可以保证排序稳定性,
// 反之如果只取 < 会导致相等元素的初始顺序颠倒
temp[k++] = sourceArr[i++];
} else {
temp[k++] = sourceArr[j++];
}
}
//将左侧数组剩余元素放入临时数组中
while (i <= middle) {
temp[k++] = sourceArr[i++];
}
//将右侧数组剩余元素放入临时数组中
while (j <= high) {
temp[k++] = sourceArr[j++];
}
//将临时数组拷贝到源数组中
System.arraycopy(temp, 0, sourceArr, low, high - low + 1);
}
}
验证排序稳定性
@Data
public class Student {
private int age;
private int sort;
}
public class StudentMergeSortTest {
@Test
void testMergeSort() {
int[] sourceArr = {1, 4, 6, 8, 9, 3, 5, 6, 8, 3, 2, 10, 5, 14, 34, 32, 23};
int length = sourceArr.length;
Student[] students = new Student[length];
for (int i = 0; i < length; i++) {
Student student = new Student();
student.setAge(sourceArr[i]);
student.setSort(i);
students[i] = student;
}
mergeSort(students, 0, length - 1);
}
private void mergeSort(Student[] sourceArr, int low, int high) {
if (low >= high) {
return;
}
int middle = (low + high) / 2;
mergeSort(sourceArr, low, middle);
mergeSort(sourceArr, middle + 1, high);
merge(sourceArr, low, middle, high);
System.out.println(Arrays.toString(sourceArr));
}
private void merge(Student[] sourceArr, int low, int middle, int high) {
Student[] temp = new Student[sourceArr.length];
int i = low;
int j = middle + 1;
int k = 0;
while (i <= middle && j <= high) {
if (sourceArr[i].getAge() < sourceArr[j].getAge()) {
temp[k++] = sourceArr[i++];
} else {
temp[k++] = sourceArr[j++];
}
}
while (i <= middle) {
temp[k++] = sourceArr[i++];
}
while (j <= high) {
temp[k++] = sourceArr[j++];
}
System.arraycopy(temp, 0, sourceArr, low, high - low + 1);
}
}