算法-归并排序

发布于:2025-06-26 ⋅ 阅读:(22) ⋅ 点赞:(0)

整体架构流程

归并算法采用分治法:将问题分解为几个规模较小但类型于原问题的子问题,递归求解这些子问题,然后再合并这些子问题的解来建立原来问题的解。
分解 分解待排序的n个元素的序列成各具有n/2个元素的两个子序列。直至子序列为1个元素。
解决 使用归并排序递归的排序两个子序列。
合并 合并两个已排序的子序列以产生已排序的答案。

技术细节

采用排序时,每次创建两个子序列

import java.util.Arrays;

public class MergeManyArray {
    public static void main(String... args) {
        int[] arr = {5, 2, 9, 1, 3, 6};
        sort(arr);
    }

    private static void sort(int[] arr) {
        System.out.println("排序前数组:" + Arrays.toString(arr));
        mergeSort(arr, 0, arr.length - 1);
        System.out.println("排序后数组:" + Arrays.toString(arr));
    }

    private static void mergeSort(int[] arr, int left, int right) {
        if (left >= right) {
            return;
        }
        //计算中间数
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }

    private static void merge(int[] arr, int left, int mid, int right) {
        System.out.println("排序参数:left = " + left + " mid = " + mid + " right=" + right);
        //左边数组长度
        int leftLength = mid - left + 1;
        //右边数组长度
        int rightLength = right - mid;
        System.out.println("排序参数:leftLength = " + leftLength + " rightLength = " + rightLength);
        //创建两个数组
        int[] leftArray = new int[leftLength];
        int[] rightArray = new int[rightLength];
        //向两个数组复制数据
        for (int i = 0; i < leftLength; i++) {
            leftArray[i] = arr[left + i];
        }
        for (int i = 0; i < rightLength; i++) {
            rightArray[i] = arr[mid + i + 1];
        }
        System.out.println("左边数据:" + Arrays.toString(leftArray));
        System.out.println("右边数据:" + Arrays.toString(rightArray));

        //设置左,右两个数组的索引
        int leftIndex = 0, rightIndex = 0;

        //升序排序
        //因排序的两个数组,每个数组元素是有序的
        //所以耽搁数组无需排序
        for (int k = left; k <= right; k++) {
            if (rightIndex >= rightLength) {
                //右边数组,已全部比较完成。
                arr[k] = leftArray[leftIndex];
                leftIndex++;
            } else if (leftIndex >= leftLength) {
                //左边数组,已全部比较完成。
                arr[k] = rightArray[rightIndex];
                rightIndex++;
            } else if (leftArray[leftIndex] <= rightArray[rightIndex]) {
                arr[k] = leftArray[leftIndex];
                leftIndex++;
            } else {
                arr[k] = rightArray[rightIndex];
                rightIndex++;
            }
        }
        System.out.println("合并排序:" + Arrays.toString(arr));
    }
}

采用排序时,采用一个数组

复用一个数组,数组长度等于当前数组长度

import java.util.Arrays;

public class MergeOneArray {
    public static void main(String... args) {
        int[] arr = {5, 2, 9, 1, 3, 6};
        sort(arr);
    }

    /**
     * 排序
     * @param arr 数组
     */
    private static void sort(int[] arr) {
        System.out.println("排序前数组:" + Arrays.toString(arr));
        int[] copy = new int[arr.length];
        mergeSort(arr, copy, 0, arr.length - 1);
        System.out.println("排序后数组:" + Arrays.toString(arr));
    }

    /**
     * 归并排序
     * @param arr 数组
     * @param copy 复制数组
     * @param left 左边索引位置
     * @param right 右边索引位置
     */
    private static void mergeSort(int[] arr, int[] copy, int left, int right) {
        if (left >= right) {
            return;
        }
        //计算中间数
        int mid = (left + right) / 2;
        mergeSort(arr, copy, left, mid);
        mergeSort(arr, copy, mid + 1, right);
        merge(arr, copy, left, mid, right);
    }

    /**
     * 排序
     * @param arr 数组
     * @param copy 复制数组
     * @param left 左边索引
     * @param mid  中间索引
     * @param right 右边索引
     */
    private static void merge(int[] arr, int[] copy, int left, int mid, int right) {
        System.out.println("排序参数:left = " + left + " mid = " + mid + " right=" + right);

        //复制数据
        for (int i = left; i <= right; i++) {
            copy[i] = arr[i];
        }
        System.out.println("复制数组:" + Arrays.toString(copy));


        //设置左,右两个数组的索引
        int leftIndex = left, rightIndex = mid + 1;

        //升序排序
        //需要比较的mid左右两边的元素是有序的
        //所以数组无需排序
        for (int k = left; k <= right; k++) {
            if (rightIndex >= right+1) {
                //右边数组,已全部比较完成。
                arr[k] = copy[leftIndex];
                leftIndex++;
            } else if (leftIndex >= mid+1) {
                //左边数组,已全部比较完成。
                arr[k] = copy[rightIndex];
                rightIndex++;
            } else if (copy[leftIndex] <= copy[rightIndex]) {
                arr[k] = copy[leftIndex];
                leftIndex++;
            } else {
                arr[k] = copy[rightIndex];
                rightIndex++;
            }
        }
        System.out.println("合并排序:" + Arrays.toString(arr));
        //为了打印更好理解,实际算法中不需要
        for (int i = left; i <= right; i++) {
            copy[i] = 0;
        }
    }
}

小结

采用单个数组,可以减少数组的创建,以提供算法性能


网站公告

今日签到

点亮在社区的每一天
去签到