归并排序

72 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第二十二天,点击查看活动详情

六、归并排序

采用分治策略。分治法将问题分 (divide) 成一些小的问 题然后递归求解,而治(conquer) 的阶段则将分的阶段得到的各答案"修补"在一 起,即分而治之。

image-20220903153717766

以上结构很像一棵完全二叉树,采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归 拆分子序列的过程

分析

上图合并了七次,下图为最后一次的合并详解:

image-20220903154707564

import java.util.Arrays;
​
/**
 * @author Kcs 2022/9/3
 */
public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
​
​
        System.out.println("要排序的数组:" + Arrays.toString(arr));
        //归并需要一个额外的空间
        int[] temp = new int[arr.length];
​
        mergeSort(arr, 0, arr.length - 1, temp);
​
        System.out.println("归并排序结果:" + Arrays.toString(arr));
    }
​
    /**
     * 分解 + 合并 过程
     */
    public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) {
            int mid = (left + right) / 2;
            // 向左递归进行分解
            mergeSort(arr, left, mid, temp);
​
            //向右递归分解
            mergeSort(arr, mid + 1, right, temp);
​
            //合并
            merge(arr, left, mid, right, temp);
        }
    }
​
​
    /**
     * 合并起来
     * @param arr 原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 做中转数组
     */
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        // 初始化i 左边有序序列的初始索引
        int i = left;
        // 初始化 j ,右边有序序列的初始索引
        int j = mid + 1;
        // 指向temp数组的当前索引
        int t = 0;
​
        // 1. 左右两边的数据,按照规则进行填充到 temp 数组,直到左右两边的有序序列,有一边完成为止
        while (i <= mid && j <= right) {
            //左边的有序序列的当前元素,小于等于右边有序序列的当前元素,即将左边的当前元素,拷贝到 temp 中
            if (arr[i] < arr[j]) {
                temp[t] = arr[i];
                t += 1;
                i += 1;
            } else {
                // 右边的比左边的元素大,填充到temp数据
                temp[t] = arr[j];
                t += 1;
                j += 1;
​
            }
        }
​
​
        // 2. 把剩余数据的一边的数据依次全部填充到 temp 中
        while (i <= mid) {
            //左边的 有序序列还有剩余元素,全部填充到 temp 中去
            temp[t] = arr[i];
            t += 1;
            i += 1;
        }
​
        while (j <= right) {
            //左边的 有序序列还有剩余元素,全部填充到 temp 中去
            temp[t] = arr[j];
            t += 1;
            j += 1;
        }
​
        // 3. 将temp 数据的元素拷贝到 arr,并不是每次拷贝所有
        t = 0;
        int tempLeft = left;
        //最后一次 tempLeft = 0 ,right = 7
        System.out.println("tempLeft=" + tempLeft + " " + "right=" + right);
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }
}