7、归并排序

内容来自刘宇波老师算法与数据结构体系课

1、归并排序

/**
 * 归并排序: O(N * logN)
 */
public class MergeSort {

    private MergeSort() {
    }

    /**
     * 归并排序
     */
    public static <E extends Comparable<E>> void sort(E[] arr) {
        sort(arr, 0, arr.length - 1);
    }

    /**
     * 归并排序 arr[l, r]
     */
    private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
        if (l >= r) return;

        int mid = l + (r - l) / 2;
        sort(arr, l, mid);     // arr[l, mid]
        sort(arr, mid + 1, r); // arr[mid + 1, r]

        merge(arr, l, mid, r);
    }

    /**
     * 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
     */
    private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r) {
        E[] temp = Arrays.copyOfRange(arr, l, r + 1);

        int p1 = 0;           // temp[0, mid - l]
        int p2 = mid + 1 - l; // temp[mid + 1 - l, r - l]
        int i = l;            // arr[l, r]

        while (p1 <= mid - l && p2 <= r - l) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
        while (p1 <= mid - l) arr[i++] = temp[p1++];
        while (p2 <= r - l) arr[i++] = temp[p2++];
    }
}

2、归并排序优化

/**
 * 归并排序: O(N * logN)
 * 优化 1: merge 条件, 对完全有序的数组 O(n)
 * 优化 2: 数据量小的时候(<=16)采用插入排序
 * 优化 3: 避免频繁的在内存中开辟空间
 */
public class MergeSortPlus {

    private MergeSortPlus() {
    }

    /**
     * 归并排序
     */
    public static <E extends Comparable<E>> void sort(E[] arr) {
        E[] temp = (E[]) new Comparable[arr.length]; // 优化 3: 避免频繁的在内存中开辟空间
        sort(arr, 0, arr.length - 1, temp);
    }

    /**
     * 归并排序 arr[l, r]
     */
    private static <E extends Comparable<E>> void sort(E[] arr, int l, int r, E[] temp) {
        if (r - l <= 15) {
            InsertionSort.sort(arr, l, r); // 优化 2: 数据量小的时候(<=16)采用插入排序
            return;
        }

        int mid = l + (r - l) / 2;
        sort(arr, l, mid, temp);     // arr[l, mid]
        sort(arr, mid + 1, r, temp); // arr[mid + 1, r]

        if (arr[mid].compareTo(arr[mid + 1]) > 0) merge(arr, l, mid, r, temp); // 优化 1: merge 条件
    }

    /**
     * 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
     */
    private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r, E[] temp) {
        System.arraycopy(arr, l, temp, l, r - l + 1);

        int p1 = l;       // temp[l, mid]
        int p2 = mid + 1; // temp[mid + 1, r]
        int i = l;        // arr[l, r]

        while (p1 <= mid && p2 <= r) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
        while (p1 <= mid) arr[i++] = temp[p1++];
        while (p2 <= r) arr[i++] = temp[p2++];
    }
}

3、自底向上归并排序

/**
 * 自底向上归并排序: O(N * logN)
 */
public class MergeSortBU {

    private MergeSortBU() {
    }

    /**
     * 自底向上归并排序
     */
    public static <E extends Comparable<E>> void sort(E[] arr) {
        int n = arr.length;
        E[] temp = (E[]) new Comparable[n];

        // 对 arr[0, n - 1] 中所有的 arr[i, i + 15], 使用插入排序法
        for (int i = 0; i < n; i += 16) {
            InsertionSort.sort(arr, i, Math.min(i + 15, n - 1));
        }

        // 遍历合并的区间长度(把 2 个区间合并成 1 个区间)
        for (int size = 16; size < n; size += size) {
            // 合并 arr[i, i + size - 1] 和 arr[i + size, i + size + size - 1]
            for (int i = 0; i + size < n; i += 2 * size) {
                if (arr[i + size - 1].compareTo(arr[i + size]) > 0) {
                    merge(arr, i, i + size - 1, Math.min(i + size + size - 1, n - 1), temp);
                }
            }
        }
    }

    /**
     * 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
     */
    private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r, E[] temp) {
        System.arraycopy(arr, l, temp, l, r - l + 1);

        int p1 = l;       // temp[l, mid]
        int p2 = mid + 1; // temp[mid + 1, r]
        int i = l;        // arr[l, r]

        while (p1 <= mid && p2 <= r) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
        while (p1 <= mid) arr[i++] = temp[p1++];
        while (p2 <= r) arr[i++] = temp[p2++];
    }
}

4、复杂度分析

元素个数为 N,那么:节点个数为 2N - 1,层数为 logN + 1

image

image

5、逆序数对

剑指 Offer 51 - 数组中的逆序对

image

public class ReversePairs {

    /**
     * 归并解法
     */
    public static int reversePairs(int[] nums) {
        return sort(nums);
    }

    private static int sort(int[] arr) {
        int[] temp = new int[arr.length];
        return process(arr, 0, arr.length - 1, temp);
    }

    private static int process(int[] arr, int l, int r, int[] temp) {
        // if (l >= r) return 0;
        if (r - l <= 15) return insertionSort(arr, l, r);

        int mid = l + (r - l) / 2;
        int left = process(arr, l, mid, temp);
        int right = process(arr, mid + 1, r, temp);

        if (arr[mid] > arr[mid + 1]) return left + right + merge(arr, l, mid, r, temp);
        return left + right;
    }

    private static int merge(int[] arr, int l, int mid, int r, int[] temp) {
        int res = 0;
        System.arraycopy(arr, l, temp, l, r - l + 1);

        int p1 = l;
        int p2 = mid + 1;
        int i = l;

        while (p1 <= mid && p2 <= r) {
            if (temp[p1] > temp[p2]) {
                // 输出逆序数对
                // for (int j = p1; j <= mid; j++) {
                //    System.out.println(String.format("[%d, %d]", temp[j], temp[p2]));
                // }
                res += mid - p1 + 1; // 后面区间的元素归并上来时, 和前面区间剩余元素形成逆序数对
                arr[i++] = temp[p2++];
            } else {
                arr[i++] = temp[p1++];
            }
        }
        while (p1 <= mid) arr[i++] = temp[p1++];
        while (p2 <= r) arr[i++] = temp[p2++];
        return res;
    }

    private static int insertionSort(int[] arr, int l, int r) {
        int res = 0;
        for (int i = l + 1; i <= r; i++) {
            int k = arr[i];
            int j;
            for (j = i; j - 1 >= l && arr[j - 1] > k; j--) {
                arr[j] = arr[j - 1];
            }
            arr[j] = k;
            res += i - j; // arr[i] 插入到了 arr[j] 的位置
        }
        return res;
    }
}
posted @ 2023-04-10 14:45  lidongdongdong~  阅读(30)  评论(0编辑  收藏  举报