排序算法学习07_归并排序(Java)

归并排序


前言:在博客写这些文章的目的用于记录所学,怕以后忘了,如果哪里写的不对欢迎指正,谢谢!!

学习目标:掌握归并排序算法的原理和思想

一、前提知识

  排序算法概念、时间复杂度。可前往此网址 排序算法学习01_算法基础介绍阅读

二、归并排序介绍

  归并排序算法,采用分治思想排序元素。它和快速排序类似,递归地对序列一分为二进行排序

三、归并排序工作原理

  归并排序也是递归地对序列一分为二进行排序。但它不像快排,边分边排。而是一直进行分解,分解到某个子序列只剩一个元素时,然后递归返回,才开始排序,这也是归并名字的由来。

  它也不像是快排在原有的基础上进行排序,而是通过申请一个临时空间(数组),该空间存放的是每次递归时中序列里的元素,我们就要在这个临时空间上比较元素,然后依次从小到大的(升序时)赋值到原数组

  先对原序列的一部分子序列排序,再把这几个部分子序列合起来再排序。递归地执行,最终返回一个排序好的序列。

  如果你学过快速排序,那么我相信你可以很快学会归并排序

四、归并排序设计思路

使用递归解决分治策略,主要思考以下两点:

  1. 分解如何对序列进行划分,也就是如何分组,分成什么样
  2. 归并如何对分组后的数据进行组合排序
  • 关于第一点,序列如何分组

    • 归并排序采用相邻分组,也就是划分组的方式,就相邻位置的元素即可为一组
    • 关于具体分组方式,接下来就要引入三个变量
      • startIndex:用于表示某个序列的起始位置
      • endIndex:用于表示某个序列最后位置
      • middleIndex:中部下标,由 startIndex + (endIndex - startIndex) / 2 所得。该变量有两层作用
        • 第一层:递归过去时,用于分离序列(分组),分解序列依靠该变量
        • 第二层:递归回来要排序时,将用于区分要进行合并排序的两个子序列的位置
  • 关于第二点,那就是排序的问题

    • 这时,就要用到临时空间

      • 临时空间首先存放子序列的元素,子序列就是从startIndexendIndex位置范围内的元素
    • 归并:归并的是什么?通过介绍我们知道,每次递归的对序列一分为二。那递归回来时,就要对这被一分为二的两个序列进行整合排序,这就是归并

      • 接下来需再引入两个变量

        • left:刚开始指向左边子序列的首位下标
        • right:刚开始指向右边子序列的首位下标
      • 这两个变量将在临时空间上移动,如何区分临时空间上的左右序列,这时派上middleIndex的用场了(刚刚讲过第二层作用)

        • left~middleIndex为左序列
        • middleIndex+1~right为右序列
      • 再接下来就是在临时空间上两边子序列比较的问题了(采用升序)

        • 首先遍历tempArr(临时空间)
          • 然后取tempArr中左子序列left指向的元素与右子序列right指向的元素比较
          • 大于时,则把right位置指向元素放入原序列,原序列的索引如何取
            • 取遍历tempArr时的下标,这就可以起到,在子序列进行排序的作用
          • 哪个子序列往原序列放入元素,那么该子序列上的扫描下标,则往后移动一步
            • 此时,我们的right就要*+1*,以判断右子序列的下个值
          • 重复此逻辑
          • 还有两种特殊情况
            • 左子序列left在移动过程中,越过中部下标middleIndex
            • 右子序列right在移动过程中,越过endIndex,也就是tempArr的最后一个元素
            • 这两种情况代表了,所在的子序列以判断完毕,可以直接把另一边序列的元素依次填入原序列
        • 图解的话大概情况如这样,就不细话了(具体情况可以看其他博主用图解将的归并)。这是一个临时空间[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdukdK18-1611061612778)(C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1611059779884.png)]

五、代码实现

package com.migu.sortingAlgorithm;

import java.util.Arrays;

/**
 * 归并排序
 */
public class MergeSort {
    public static void main(String[] args) {
        int array[] = {3,2,11,5,1,7,9,4,-1,-8,0};

        sort(array);
        System.out.println(Arrays.toString(array)); // 调用工具类输出
    }
    public static void sort(int[] arr) {
        int[] tempArr = new int[arr.length]; // 先申请一个临时空间,用于存放每次归并好的序列
        sort(arr,tempArr,0,arr.length-1);
    }

    /**
     * 采用分治思想,以递归方式
     * @param arr 要排序数组
     * @param tempArr 临时空间
     * @param startIndex 子序列起始位置
     * @param endIndex 子序列末尾位置
     */
    private static void sort(int[] arr,int[] tempArr,int startIndex,int endIndex){
        if(endIndex <= startIndex){
            return;
        }
        // 中部下标,依靠该值递归的一分为二分解子序列
        int middleIndex = startIndex + (endIndex - startIndex) / 2;

        // 分解
        sort(arr,tempArr,startIndex,middleIndex);
        sort(arr,tempArr,middleIndex + 1,endIndex);

        // 归并(排序)
        mergeSort(arr,tempArr,startIndex,middleIndex,endIndex);
    }

    /**
     * 归并
     * @param arr 要排序数组
     * @param tempArr 临时空间
     * @param startIndex 归并起始位置
     * @param middleIndex 划分左右序列
     * @param endIndex 归并终止位置
     */
    private static void mergeSort(int[] arr,int[] tempArr,int startIndex,int middleIndex,int endIndex) {
        // 复制要合并的数据到临时空间
        for (int s = startIndex; s <= endIndex; s++) {
            tempArr[s] = arr[s];
        }

        int left = startIndex;// 左边首位下标,在左子序列扫描,向右移动
        int right = middleIndex + 1;// 右边首位下标,在右子序列扫描,向右移动
        // 遍历临时空间
        for (int k = startIndex; k <= endIndex; k++) {
            // 如果左子序列下标在移动过程中大于中部下标,证明左边的数据已经排完了。
            if(left > middleIndex){
                // 那么只需把右子序列元素一直填入原序列
                arr[k] = tempArr[right++];
                // 右边同理
            } else if (right > endIndex){
                arr[k] = tempArr[left++];
            } else if (tempArr[right] < tempArr[left]){  // 实现升序  '<'
                arr[k] = tempArr[right++];// 将右子序列值排入
            } else {
                arr[k] = tempArr[left++];// 将左子序列值排入
            }
        }
    }
}

六、时间复杂度

  该算法的时间复杂度为 O(nlogn) ,区别于之前所讲的算法,归并排序算法还需要一个额外的空间,O(1)

七、总结

  归并排序算法,采用分治思想,对序列一直进行一分为二的分解,然后分解到最后,回来时再进行合并排序,是一种十分高效的排序算法。且他是稳定的

  网上所说这是利用一种什么二叉树来排序,我还没学过,就不细讲了,哎呀我好菜,如果你能一步一步看到这里,真的万分感谢,最近学快排和归并头疼死我了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值