JAVA-归并排序(图解)

一、基本思路

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

归并排序拆分合并过程其实是递归实现的,所以我们要根据中间绿色的线,把这个图看为对折的图 :

 二、归并排序的实现

1.拆解过程

 我们先来做分解(递)的过程:

 

 观察发现

对于左子树,left还是在原来位置,right是原来mid的位置

对于右子树,left在mid+1位置,right还是原来位置

mid可以通过(left+right) / 2来获取  

那么我们现在可以写出这样的代码:

    public static void mergeSort(int[] array) {
        mergeSortFun(array,0,array.length-1);
    }

    private static void mergeSortFun(int[] array,int left,int right) {
    
        int mid = (left + right) / 2;

        //左
        mergeSortFun(array,left,mid);
        //右
        mergeSortFun(array,mid+1,right);

        //合并……
       

    }

 但是递归必须要有结束条件,那么我们需要按照这个思想继续往下执行,来观察结束条件:

当为分解为一个元素的时候left和right会重合,为了能更好理解我再代入一个案例:

 那么我们就知道了结束条件:

    public static void mergeSort(int[] array) {
        mergeSortFun(array,0,array.length-1);
    }

    private static void mergeSortFun(int[] array,int left,int right) {
        //结束条件
        //为了预防会有超出的情况所以多写一个大于
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;

        //左
        mergeSortFun(array,left,mid);
        //右
        mergeSortFun(array,mid+1,right);

        //合并……
    }

2.合并过程

再来做合并的过程 (归)

提醒:

因为我们是归的过程,所以其实下面6 10排完序开辟的方法,其实就是上面10 6同一个开辟的空间。 

 

 合并思路:​​​​​​​

 

 

排序虽然完成了但是,这是在tmp临时数组中排的,还需要拷贝回源来的数组 :

加上这样一段代码行吗,是不行的。不是所有情况都是从0下标开始

这就是整套合并逻辑,由于单个元素合并比较特殊,我再将上述图的下一步合并画一下方便理解:

3.代码

    /**
     * 时间复杂度:0(N * log2N)
     * 空间复杂度:O(N)
     * 稳定性:稳定的排序
     * @param array
     */
    public static void mergeSort(int[] array) {
        mergeSortFun(array,0,array.length-1);
    }

    private static void mergeSortFun(int[] array,int left,int right) {
        //结束条件
        //为了预防会有超出的情况所以多写一个大于
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;

        //左
        mergeSortFun(array,left,mid);
        //右
        mergeSortFun(array,mid+1,right);

        //合并
        merge(array,left,mid,right);

    }
    private static void merge(int[] array,int left,
                              int mid,int right) {
        //建立临时数组
        int[] tmp = new int[right - left + 1];
        //临时数组下标
        int k = 0;
        //第一组数据的范围
        int s1 = left;
        int e1 = mid;
        //第二组数据的范围
        int s2 = mid+1;
        int e2 = right;

        //while (s1 < e1 && s2 < e2) 不稳定的写法
        while (s1 <= e1 && s2 <= e2) {
            if(array[s1] <= array[s2]) {
                tmp[k] = array[s1];
                k++;
                s1++;
            }else {
                tmp[k] = array[s2];
                k++;
                s2++;
            }
        }

        //看哪一个数组没有拷贝完
        while (s1 <= e1) {
            tmp[k] = array[s1];
            k++;
            s1++;
        }
        while (s2 <= e2) {
            tmp[k] = array[s2];
            k++;
            s2++;
        }

        //将临时数组的数据拷贝到源数组当中
        for (int i = 0; i < k; i++) {
            array[left + i] = tmp[i];
        }
    }

4.非递归实现

    /**
     * 非递归实现归并排序
     * @param array
     */
    public static void mergeSortNor(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i = i + 2*gap) {
                int left = i;
                int mid = left+gap - 1;
                if(mid >= array.length) {
                    mid = array.length-1;
                }
                int right = mid+gap;
                if(right >= array.length) {
                    right = array.length-1;
                }
                merge(array,left,mid,right);
            }
            gap *= 2;
        }
    }

### Java 实现归并排序图解及可视化教程 #### 一、归并排序简介 归并排序是一种基于分治法的高效稳定排序算法。该算法将待排序序列分为若干子序列,分别对这些子序列进行排序;再依次合并已有序的子序列,最终得到完全有序的序列。 #### 二、具体操作流程 1. **分解**:把长度为n的输入序列分成两个大致相等的子序列; 2. **解决**:递归地对这两个子序列调用归并排序函数,直到每个子序列只剩下一个元素为止; 3. **合并**:将已经排好序的子序列逐步合并成更大的有序序列。 #### 三、图示过程展示 假设有一个无序数组`{8, 4, 5, 7, 1, 3, 6, 2}`需要被排序: - 初始状态: - 数组未经过任何处理 `{8, 4, 5, 7, 1, 3, 6, 2}` - 经过分割后的第一次迭代: - 左半边分割结果 `[8, 4], [5, 7]` - 右半边分割结果 `[1, 3], [6, 2]` - 对每一对相邻的小数组执行内部排序后继续拆分: - 接下来会形成更小规模的数据集 `[[8],[4]], [[5],[7]]`, `[[1],[3]], [[6],[2]]` - 当不能再进一步细分时开始回溯组合: - 合并最底层的结果获得新的局部有序集合 `[(4,8)], [(5,7)]`, `[(1,3)], [(2,6)]` - 将上述四个部分再次配对比较大小完成新一轮融合: - 得到更大范围内的顺序排列 `[(4,5,7,8)], [(1,2,3,6)]` - 执行最后一次汇总动作即完成了整个列表的整体升序调整: - 结果变为 `(1,2,3,4,5,6,7,8)` 完全满足预期目标[^1] #### 四、代码实现样例 以下是使用Java编写的简单版本归并排序程序: ```java public class MergeSort { private static void merge(int[] arr, int l, int m, int r){ // 创建临时辅助存储空间用于保存左右两侧数据副本 int n1 = m - l + 1; int n2 = r - m; /* 下标从0开始 */ int L[] = new int[n1]; int R[] = new int[n2]; for (int i=0; i<n1; ++i) L[i] = arr[l + i]; for (int j=0; j<n2; ++j) R[j] = arr[m + 1+ j]; /* 开始正式合并不同区间的数值 */ int i = 0, j = 0; int k = l; while (i < n1 && j < n2){ if (L[i] <= R[j]){ arr[k] = L[i]; i++; } else{ arr[k] = R[j]; j++; } k++; } /* 如果左边还有剩余则全部复制过来 */ while (i < n1){ arr[k] = L[i]; i++; k++; } /* 若右边有残留同样追加至末端 */ while (j < n2){ arr[k] = R[j]; j++; k++; } } private static void sort(int[] arr, int l, int r){ if (l < r){ int m = (l+r)/2; // 分别针对前后两段实施相同逻辑直至触底反弹 sort(arr, l, m); sort(arr ,m+1, r); // 随着递归返回上层节点逐渐拼接起来成为完整的有序表列 merge(arr, l, m, r); } } public static void main(String args[]){ int arr[] = {8, 4, 5, 7, 1, 3, 6, 2}; System.out.println("原始数组:"); printArray(arr); sort(arr, 0, arr.length-1); System.out.println("\n排序之后:"); printArray(arr); } /** 辅助打印功能方便观察变化情况*/ static void printArray(int arr[]){ int n = arr.length; for (int i=0; i<n; ++i) System.out.print(arr[i] + " "); System.out.println(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值