归并排序

163 阅读2分钟

归并排序

原理

  1. 将一个无序数据拆分成两个,然后分别对这两个子数组进行排序,子数组排序完成后,将数组合并为一个数据
  2. 递归上述动作

这里仅仅表述了归并排序的基本原理,并非真的每次都要申请新的内存空间在存储拆分开的两个数组,其实如果每次都取中间位置进行拆分,通过递归,最终会拆分成两个最小子数组,每个数组都只有一个元素

其实并不是真的拆分成连个数组,而是获取了原数组中的下标范围,来标识两个数组

实现

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);
    }
}