归并排序与对数器

202 阅读3分钟

归并排序是一种经典的排序算法,需要掌握(毕竟是冯诺依曼在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的过程把排序信息记录了下来,传递了下去。