七大排序之归并排序

2023-11-15


什么是归并排序?

归并排序的思想:将原数组不断拆分,一直拆到每个子数组只有一个元素时,第一阶段结束。然后开始“并”,将相邻两个数组合并为一个有序的数组,直到整个数组有序

归并排序时间复杂度:归并排序是一个稳定的 O(nlogn) 排序算法,此处的稳定指的是时间复杂度稳定且归并排序也是一个稳定性排序算法

时间复杂度稳定:归并排序拆分的过程类似于树的结构,递归的深度就是我们拆分数组所用的时间,就是树的深度,为O(logn)。而合并两个数组的过程 merge 操作就是一个数组的遍历过程,为O(n)。

归并排序图解:

在这里插入图片描述
合并过程图解:

归并

归并排序代码

代码如下:

public static void mergeSort(int[] arr){
        mergeSortInternal(arr,0,arr.length-1);
    }
    private static void mergeSortInternal(int[] arr, int l, int r) {
        if (l>=r){
            return;
        }
        int mid=l+(r-l)/2;
        //将原数组拆分成左右两个小区间
        mergeSortInternal(arr,l,mid);
        mergeSortInternal(arr,mid+1,r);
        merge(arr,l,r,mid);
    }
    private static void merge(int[] arr, int l, int r, int mid) {
        //1.定义一个临时数组,将arr里的元素拷贝进去
        int[] aux=new int[r-l+1];
        for (int i = 0; i < aux.length; i++) {
            aux[i]=arr[i+l];
        }
        int i=l;  //i为左侧小数组的起始位置
        int j=mid+1; //j为右侧小数组的起始位置
        for (int k=l;k<=r;k++){ //k遍历的是arr数组
            if (i>mid){  //左侧小数组已经处理完了,直接将右侧剩下的元素拷贝过去
                arr[k]=aux[j-l];
                j++;
            }else if(j>r){ //右侧小数组已经处理完了,直接将左侧剩下的元素拷贝过去
                arr[k]=aux[i-l];
                i++;
            }else if(aux[i-l]<=aux[j-l]){//当左侧区间的元素大于右侧区间的元素时
                arr[k]=aux[i-l];
                i++;
            }else{
                arr[k]=aux[j-l];
                j++;
            }
        }
    }

归并排序的优化:

  • 当左右两个子区间走完子函数后,如果此时 arr[mid] < arr[mid+1],说明 arr[mid] 已经时左区间的最大值了,arr[mid+1] 已经是右区间的最小值了,整个区间已经有序,不需要再执行 merge 操作了!!!
  • 在小区间上,我们可以直接使用插入排序来优化,可以减少递归的次数!!!

优化后的代码如下:

public static void mergeSort(int[] arr){
        mergeSortInternal(arr,0,arr.length-1);
    }
    private static void mergeSortInternal(int[] arr, int l, int r) {
        //优化1:小区间上使用插入排序
        if (r-l<=15){
            insertionSort(arr,l,r);
            return;
        }
        int mid=l+(r-l)/2;
        mergeSortInternal(arr,l,mid);
        mergeSortInternal(arr,mid+1,r);
        //优化2:只有两个区间还存在元素先后顺序不同时才合并
        if (arr[mid]>arr[mid+1]) {
            merge(arr, l, r, mid);
        }
    }
    private static void merge(int[] arr, int l, int r, int mid) {
        int[] aux=new int[r-l+1];
        for (int i = 0; i < aux.length; i++) {
            aux[i]=arr[i+l];
        }
        int i=l;
        int j=mid+1;
        for (int k=l;k<=r;k++){
            if (i>mid){
                arr[k]=aux[j-l];
                j++;
            }else if (j>r){
                arr[k]=aux[i-l];
                i++;
            }else if (aux[i-l]<=aux[j-l]){
                arr[k]=aux[i-l];
                i++;
            }else{
                arr[k]=aux[j-l];
                j++;
            }
        }
    }
    public static void insertionSort(int[] arr,int l,int r){
        for (int i = l+1; i <=r ; i++) {
            for (int j = i; j >0 ; j--) {
                if (arr[j]>=arr[j-1]){
                    break;
                }else{
                    swap(arr,j,j-1);
                }
            }
        }
    }
    public static void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }

归并排序相关习题

148.排序链表

题目描述如下:

在这里插入图片描述
代码如下:

public ListNode sortList(ListNode head){
        if (head==null||head.next==null){
            return head;
        }
        ListNode mid=getMidNode(head);
        ListNode rightNode=mid.next;
        mid.next=null;
        //递归拆分链表
        ListNode l=sortList(head);
        ListNode r=sortList(rightNode);
        return merge(l,r);
    }
    //合并
    private ListNode merge(ListNode l, ListNode r) {
        ListNode dummyHead=new ListNode(0);
        ListNode cur=dummyHead;
        while (l!=null&&r!=null){
            if (l.val<r.val){
                cur.next=l;
                l=l.next;
                cur=cur.next;
            }else{
                cur.next=r;
                r=r.next;
                cur=cur.next;
            }
        }
        if (l==null){
            cur.next=r;
        }
        if (r==null){
            cur.next=l;
        }
        return dummyHead.next;
    }
    //获取链表中间节点(快慢指针)
    public ListNode getMidNode(ListNode head){
        ListNode fast=head.next.next;
        ListNode low=head;
        while (fast!=null&&fast.next!=null){
            fast=fast.next.next;
            low=low.next;
        }
        return low;
    }

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

题目描述如下:

在这里插入图片描述
解题思路:

在这里插入图片描述
代码如下:

public int reversePairs(int[] nums) {
        return reversePairsHelper(nums,0,nums.length-1);
    }
    private int reversePairsHelper(int[] nums, int l, int r) {
        if (l>=r){
            return 0;
        }
        int mid=l+(r-l)/2;
        //1.先求出左区间的逆序对个数
        int left=reversePairsHelper(nums, l, mid);
        //2.求出右区间的逆序对个数
        int right=reversePairsHelper(nums, mid+1, r);
        if (nums[mid]>nums[mid+1]){
            //如果此时左右区间还存在逆序,合并
            return merge(nums,l,mid,r)+left+right;
        }
        //此时左右区间已经有序,逆序对个数就是左右区间逆序对个数之和
        return left+right;
    }
    //合并,并且返回合并过程中逆序对的个数
    private int merge(int[] nums, int l, int mid, int r) {
        //合并过程中产生的逆序对
        int count=0;
        int[] aux=new int[r-l+1];
        for (int i = 0; i < aux.length; i++) {
            aux[i]=nums[i+l];
        }
        int i=l;
        int j=mid+1;
        for (int k=l;k<=r;k++){
            if (i>mid){
                nums[k]=aux[j-l];
                j++;
            }else if (j>r){
                nums[k]=aux[i-l];
                i++;
            }else if (aux[i-l]<=aux[j-l]){
                nums[k]=aux[i-l];
                i++;
            }else{
                //左区间元素大于右区间元素,构成逆序对
                //记录逆序对的个数
                count+=mid-i+1;
                nums[k]=aux[j-l];
                j++;
            }
        }
        return count;
    }

总结

以上就是今天要讲的内容,本文介绍了七大排序中的归并排序,包括归并排序的思路、代码和相关习题,如果你觉得有收获的话,那就留下你的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

七大排序之归并排序 的相关文章

随机推荐

  • MyBatis choose、when和otherwise标签简介说明

    转自 MyBatis choose when和otherwise标签简介说明 MyBatis 中动态语句 choose when otherwise其功能 同Java中的switch case default语句相同 但是因为MyBatis
  • stata 线性回归分析基本操作

    一 线性回归基本命令 regress y x1 x2 红色表示该命令可简写为红色部分 以 Nerlove 数据为例 数据附后文 regress lntc lnq lnpf lnpk lnpl 表上半部分为方差分析表 包括回归平方和 残差平方
  • python对excel增删改查语句_python对 MySQL 数据库进行增删改查的脚本

    coding utf 8 import pymysql import xlrd import codecs 连接数据库 conn pymysql connect host 127 0 0 1 port 3306 user root pass
  • 不显示头像服务器问题,完美解决Gravatar头像不显示的问题

    最近一段时间 感觉我的博客打开速度很慢 页面总是加载不完 仔细检查发现gravatar头像不显示了 再一搜才知道原来gravatar的头像服务器被那啥了 对于隔三差五出现这种情况 相信各位已经无力吐槽 只能在心里默念一句 祝病魔早日 原博主
  • HarmonyOSd第一次任务

    JS FA 应用的 JS 模块 entry src main js module 的典型开发目录结构如下 目录结构中文件分类如下 hml 结尾的 HML 模板文件 这个文件用来描述当前页面的文件布局结构 css 结尾的 CSS 样式文件 这
  • Centos 磁盘根目录扩容

    Centos磁盘根目录扩容 1 扩容前检查 命令 df Th or df h 我们要扩张磁盘空间的就是挂载点为 的这个 2 添加sda磁盘空间查询磁盘 命令 fdisk l 其实 我们可以将sda的磁盘新增空间分配给处于sda的 挂载目录
  • 折线图横坐标怎么设置_Excel折线图的横坐标如何设置?方法超简单,赶快学起来...

    在我们平时使用Excel表格来进行各种数据的编辑工作时 我们往往会因为某一些实际的需求 需要在表格当中插入一些图表 以此来更加清晰 直观的展现此时表格数据当中的内容 而在Excel表格当中插入折线图 相信这是很多小伙伴都会做的工作 尤其是想
  • TCL变量

    目录 简单变量 数组 相关命令 set unset append和incr 简单变量 一个 TCL 的简单变量包含两个部分 名字和值 名字和值都可以是任意字符串 例如一个名为 1323 7 hdgg 的变量在 TCL 中都是合法的 不过为了
  • SSD,PCI-E,NVMe,M.2分类详解

    SSD PCI E NVMe M 2分类详解 首先说一下目前固态硬盘常用的两个接口 与主板相连的接口形状 SATA3和M 2 1 采用SATA3接口 目前机械硬盘采用的接口方式 的固态硬盘 在传输方式上与SATA3 的机械硬盘一样 速度的提
  • 如何使用Pandas的ExcelWriter进行excel操作

    pandas ExcelWriter定制格式 定制表头 渲染颜色等 非ExcelWriter标准的创建模式 ExcelWriter这个插件有个坑 就是已经设置好的格式是无法更改的 因此 由pandas转成excel的时候 必须将格式清除 尤
  • scanf语句的使用和执行原理

    scanf语句的使用和执行原理 1 如何使用scanf 2 scanf语句的原理 1 如何使用scanf d说明我们现在要读入一个整数了 scanf这个函数会读入一个整数 读到的结果赋值给指定变量 要注意指定变量前面的 scanf d pr
  • VUE中使用高德地图(原生UI,信息窗体内部事件监听)

    VUE中使用高德地图 原生UI 先吐槽一下 本人的环境是基于vue3 0的项目 上一位参与项目的同事使用的事vue amap 因工作需要 另外一位同事去了别的项目 所以这个万恶的项目由本人自己维护 就是再本周新增了需求 根据不用大区的用户进
  • ruoyi若依mybatis升级为mybatis-plus

    一 添加mybatis plus依赖 删除mybatis依赖 根目录下的pom文件 更改前
  • 高可用性H.A.(High Availability)

    高可用性 H A High Availability 指的是通过尽量缩短因日常维护操作 计划 和突发的系统崩溃 非计划 所导致的停机时间 以提高系统和应用的可用性
  • html / css 基础面试题 --- 页面导入时,使用link与@import有什么区别?

    页面导入时 使用link与 import有什么区别 标签和 import指令都可以用于在HTML文档中导入CSS样式表 尽管它们都可以实现相同的目的 但它们之间还是存在一些差异 1 加载顺序 当浏览器解析到标签时 会立即下载并应用样式表 这
  • Sigmoid函数使用教程

    Sigmoid函数是一种常用的激活函数 它将输入值映射到一个范围在0到1之间的连续输出 Sigmoid函数的公式如下 scss f x 1 1 exp x 以下是使用Sigmoid函数的Python示例代码 pythonCopy code
  • 六、MATLAB入门—文件操作

    文章目录 前言 一 文件的打开与关闭 1 1 文件的打开 1 2 文件的关闭 二 文件的读写操作 2 1 二进制文件的读写操作 2 2 文本文件的读写操作 三 数据文件定位 总结 前言 经过前面一段时间的学习 相信大家已经能较为熟练的在MA
  • Mini AHRS 姿态解算说明

    本文旨在讲解以下内容 1 加速度 2 陀螺仪 3 磁力计 0 序言 一直想写篇文章关于姿态解算原理的 使用尽量通俗的语句说明如何从加速度计和陀螺仪的数据 融合得到载体的姿态角 无奈自己的水平有限 一直搁置 淡泊以明志 宁静以致远 人总是要逼
  • 计算机网络实验报告 静态路由的配置

    实验名称 静态路由的配置 一 实验目的 1 掌握路由器的配置 2 学会配置静态路由 3 实现静态路由的不同网络间的互通 二 实验内容 1 搭建拓扑图 2 网络拓扑节点IP配置 3 静态路由配置实现不同网络的通信 三 实验环境 GNS3是一款
  • 七大排序之归并排序

    文章目录 什么是归并排序 归并排序代码 归并排序相关习题 148 排序链表 剑指 Offer 51 数组中的逆序对 总结 什么是归并排序 归并排序的思想 将原数组不断拆分 一直拆到每个子数组只有一个元素时 第一阶段结束 然后开始 并 将相邻