【数据结构与算法】十大经典排序算法-归并排序

news2025/1/21 6:36:12

🌟个人博客:www.hellocode.top
🏰Java知识导航:Java-Navigate
🔥CSDN:HelloCode.
🌞知乎:HelloCode
🌴掘金:HelloCode
⚡如有问题,欢迎指正,一起学习~~


当谈到高效的排序算法时,归并排序是一个备受推崇的选择。归并排序是一种分治算法,它将一个大问题分解成若干个小问题,然后逐个解决这些小问题,并将它们合并成一个整体的解。

基本思想

这里采用五分钟学算法大佬的图解,十分清晰

  1. 分解阶段: 将待排序数组分成两个相等或近似相等的子数组,不断将问题规模缩小。
  2. 解决阶段: 递归地对两个子数组进行排序,直到子数组的长度为1(即已有序)。
  3. 合并阶段: 将排好序的子数组合并成一个有序的数组,这是归并排序的核心操作。

三个阶段,涉及到递归就比较难理解(个人感觉),最好配合动画好好理解理解。主要就是分解和归并(将大问题分解为小问题,再通过归并过程合并并排序)

代码实现

每次随便写数组挺麻烦的,后续就都使用我写的工具类来生成随机数组了~

归并排序相对难理解一些,主要涉及到分解和归并,将待排序数组一个个拆分,直到拆分为1个1组(无需排序),接下来就是自底向上逐个合并,在合并的同时进行排序操作,最终合并好的就是有序的数组了

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月15日 19:33
 */
public class MergeSort {
    public static void main(String[] args) {
        // 生成容量为15的由100以内随机数构成的int数组(用到了自己写的一个工具类)
        int[] arr = ArrayUtil.randomIntArray(15, 100);
        System.out.println("原数组:" + Arrays.toString(arr));
        mergeSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    private static void mergeSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return; // 数组为空或只有一个元素,无需排序
        }
        // 创建临时数组(避免多次创建)
        int[] temp = new int[arr.length];
        // 递归入口
        sort(arr, temp, 0, arr.length - 1);
    }

    // 拆分
    private static void sort(int[] arr, int[] temp, int left, int right) {
        // 递归出口
        if (left >= right) {
            return;
        }
        // 记录中间值(左边数组的末尾,+1为右边数组的起始)
        int mid = left + ((right - left) >> 1);
        // 开始拆分
        sort(arr, temp, left, mid);
        sort(arr, temp, mid + 1, right);
        // 归并
        merge(arr, temp, left, mid, right);
    }

    // 归并(排序)
    private static void merge(int[] arr, int[] temp, int left, int mid, int right) {
        // 记录临时数组索引
        int p = left;
        // 左半边起始索引
        int i = left;
        // 右半边起始索引
        int j = mid + 1;
        // 开始归并
        // 两边都还有的情况
        while(i <= mid && j <= right){
            if(arr[i] <= arr[j]){
                temp[p++] = arr[i++];
            }else{
                temp[p++] = arr[j++];
            }
        }
        // 左边还有剩余的情况(直接加到临时数组末尾)
        while(i <= mid){
            temp[p++] = arr[i++];
        }
        // 右边还有剩余的情况(直接加到临时数组末尾)
        while(j <= right){
            temp[p++] = arr[j++];
        }
        // 将临时数组拷贝回原数组
        for(int k = left; k <= right; k++){
            arr[k] = temp[k];
        }
    }
}

测试:

原数组:[83, 49, 19, 75, 26, 64, 59, 60, 11, 97, 36, 41, 17, 31, 69]
排序后:[11, 17, 19, 26, 31, 36, 41, 49, 59, 60, 64, 69, 75, 83, 97]

生成随机数组的工具类:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月13日 20:01
 */
public class ArrayUtil {
    private static final Random RANDOM = new Random();
    
    public static int[] randomIntArray(int capacity,int max){
        // 创建capacity大小的数组(1~max)
        int[] res = new int[capacity];
        for(int i = 0; i < capacity; i++){
            res[i] = RANDOM.nextInt(max) + 1;
        }
        return res;
    }
}

优化

归并排序本身是一个稳定且效率较高的排序算法,但还是有一些优化方法可以进一步提升性能:

  1. 插入排序优化: 对于小规模子数组,使用插入排序可以提高性能。当子数组长度小于一定阈值时,切换到插入排序可以减少递归调用开销。
  2. 自底向上归并: 在归并阶段,可以使用自底向上的方法,避免递归调用,从而减少栈空间的使用。
  3. 优化合并操作: 在合并阶段,如果左子数组的最大元素小于右子数组的最小元素,可以直接跳过合并操作,因为两个子数组已经有序。

总结

优点

  1. 归并排序稳定且适用于各种数据类型。
  2. 具有稳定的时间复杂度O(n log n),适用于大规模数据排序。
  3. 分治思想使其易于理解和实现,同时也为优化提供了空间。

缺点

  1. 归并排序需要额外的存储空间来存储临时数组,空间复杂度较高。
  2. 在小规模数据排序时,性能稍逊于快速排序。

复杂度

  • 时间复杂度:平均情况、最好情况和最坏情况下的时间复杂度均为O(n log n)。
  • 空间复杂度:空间复杂度为O(n),需要额外的存储空间来存储临时数组。

使用场景

  • 归并排序适用于需要稳定排序的场景,对性能有一定要求。
  • 特别适合用于外部排序,如在磁盘上对大文件进行排序。

通过分治思想,归并排序将排序问题分解为小问题,并通过合并操作将它们逐步解决,从而实现高效且稳定的排序。这使得归并排序成为计算机科学中重要的算法之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/883829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基本变量与引用变量的区别

基本数据类型创建的变量&#xff0c;称为基本变量&#xff0c;该变量空间中直接存放的是其所对应的值&#xff1b; 而引用数据类型创建的变量&#xff0c;一般称为对象的引用&#xff0c;其空间中存储的是对象所在空间的地址。 public static void func() { int a 10; int b …

vue3 如果切换角色后权限不同 怎么清空之前添加动态路由。

项目中切换角色后发现会保留前面一个角色的权限&#xff0c;方法一是到login页面&#xff0c;权限重新reload&#xff0c;不过这样确实会影响体验&#xff0c;如果不采用此方案的话&#xff0c;你可以看下我从发现问题到解决问题的思路&#xff1a; 1、首先要找到一个初始状态…

光耦继电器:实现电气隔离的卓越选择

光耦继电器是一种常用的电子元件&#xff0c;用于实现电气隔离和信号传输。在工业控制、自动化系统和电力电子等领域&#xff0c;光耦继电器具有独特的特点和优势。本文将从可靠性、隔离性、响应速度和适应性等方面对光耦继电器的特点进行概述。 光耦继电器是一种典型的固态继电…

24、springboot的自动配置01--类条件注解@ConditionalOnClass、bean条件注解@ConditionalOnBean

springboot的自动配置 ★ 自动配置 Spring Boot的自动配置通常可根据依赖库自动触发——当Spring Boot检测到项目中包含某些框架的JAR包时&#xff0c;Spring Boot就会触发自动配置。其实通过EnableAutoConfiguration注解来启动▲ 其实你用到SpringBootApplication&#xff0…

vmware添加额外网卡

为vmware虚拟机添加额外网卡 vmware 配置管理界面配置系统内配置查看系统中的网卡状态启用网卡重启网络修改IP地址 vmware 配置管理界面配置 关闭运行的的系统。 编辑虚拟机设置—》添加–》选择网络适配器 选择网络适配器的模式 系统内配置 查看系统中的网卡状态 第一…

使用element UI 的el-upload上传图片并携带参数的用法

直接看代码&#xff1a;前端实现 <div class"upload"><el-uploadclass"upload-demo"name"upload_name":data"{user_name:user_name}"action"http://localhost:8000/api/deal_pest_Image":show-file-list"fal…

Linux学习之iptables规则基本演示

cat /etc/redhat-release看到操作系统是CentOS Linux release 7.6.1810&#xff0c;uname -r看到内核版本是3.10.0-957.el7.x86_64&#xff0c;iptables --version可以看到iptables版本是v1.4.21。 iptables的filter表 iptables -t filter 命令 规则链 规则 动作是iptables的…

实现定时发送天气信息到企微群

场景描述&#xff1a; 每天定时自动发送天气信息到企业微信群。通过Aboter如何实现呢&#xff1f; 步骤&#xff1a; 在【应用市场 > IPaaS应用 】中找到企微群定时发送天气信息的模板应用&#xff0c;点击【安装】。 在【IPaaS应用】流程列表中找到刚安装好的模板应用&…

【UE】Web Browser内嵌网页的使用

零、准备 1.在Edit菜单打开插件界面 搜索Web Browser并勾选&#xff0c;按提示重启引擎。 2.在资源窗口右键创建Widget Blueprint&#xff0c;并打开 3.搜索canvas panel 并拖拽到下方 4.在实验分类中找到Web Browser拖拽到Canvs Panel下 4.选中WebBrowser在右侧细节面板中…

复习1-2天【80天学习完《深入理解计算机系统》】第六天

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

PSM-DID方法stata操作详解:命令代码、样例数据、参考文献

PSM-DID方法stata操作详解&#xff1a;命令、数据、文献 1、内容&#xff1a;PSM-DID方法的Stata数据、命令、文献&#xff1b;传统DID的Stata数据、命令代码、文献&#xff1b;倾向得分匹配的stata数据、命令代码、DID方法需要满足的五个条件检验代码 2、方法说明&#xff1…

用于量子通信和互联网的光量子芯片

近年来&#xff0c;新兴的光量子芯片在量子通信和量子互联网领域取得了重大进展。光量子芯片芯片具有可扩展、稳定和低成本等特点&#xff0c;为微型化应用开辟了新的可能性。 7月14日&#xff0c;一篇发表在《light: science & applications》的文章概述了用于量子通信的光…

Doris2.0时代的一些机遇和挑战!

300万字&#xff01;全网最全大数据学习面试社区等你来&#xff01; 上个周五的时候&#xff0c;Doris官宣了2.0版本&#xff0c;除了在性能上的大幅提升&#xff0c;还有一些特性需要大家特别关注。 根据官网的描述&#xff0c;Doris在下面领域都有了长足进步&#xff1a; 日志…

AI百度文心一言大语言模型接入使用(中国版ChatGPT)

百度文心一言接入使用&#xff08;中国版ChatGPT&#xff09; 一、百度文心一言API二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、百度文心一言API 基于百度文心一言语言大模型…

【Java】多线程(初阶)

多线程初阶 认识线程线程的概念线程和进程的区别Java 的线程 和 操作系统线程 的关系创建线程方法1 继承Thread类方法2 实现Runnable接口其他变形匿名内部类创建Thread子类对象匿名内部类创建Runnable子类对象lambda表达式创建Runnable子类对象 Thread类及常见方法Thread的常见…

【C语言进阶(3)】高阶指针1

文章目录 字符指针指针数组指针数组的使用 数组指针数组指针的使用 数组传参和指针传参一维数组传参二维数组传参一级指针传参二级指针传参 函数指针 指针的概念 内存单元是有编号的&#xff0c;编号 地址 指针指针本质上是个用来存放地址的变量&#xff0c;地址是唯一的用来…

k-d Tree KD 树 交替建树

这个算法文字不太容易说明白&#xff0c;建议大家看视频&#xff0c;我只是抄下来做个笔记&#xff0c;方便回忆。 视频地址&#xff1a;141 k-d Tree KD 树 交替建树_哔哩哔哩_bilibili KD树的应用 kd树主要应用于多维空间关键数据的搜索&#xff1a; 最邻近搜索&#xff1…

动态规划:最大正方形

题目 在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内&#xff0c;找到只包含 ‘1’ 的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”]…

Django实现音乐网站 ⑾

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是前端开发前的一些必要配置和首页展示开发。 目录 配置应用路由 创建应用路由文件 应用路径加入项目路径 创建项目模板 创建项目及应用模板路径 设置模板路径 设置静态资源路径 创建静态资源路径 配置静态…