详解十大经典排序算法(五):归并排序(Merge Sort)

news2024/11/27 12:41:20

算法原理

归并排序的核心思想是将一个大的数组分割成多个小的子数组,然后分别对这些子数组进行排序,最后将排序后的子数组合并起来,得到一个有序的大数组。

算法描述

归并排序(Merge Sort)是一种经典的排序算法,其原理基于分治(Divide and Conquer)策略。它的核心思想是将一个大问题分割成多个小问题,解决小问题后再将它们合并以得到最终结果。

具体步骤如下:

  1. 分割(Divide):将待排序的数组递归地分割成两个子数组,直到每个子数组只包含一个元素。

  2. 排序(Conquer):对每个子数组进行排序,通常使用递归来排序。

  3. 合并(Merge):将排好序的子数组合并成一个新的有序数组。

  4. 重复步骤2和步骤3,直到整个数组都被排序。

动画演示

在这里插入图片描述

代码实现

 public static void mergeSort(int[] array) {
        if (array == null || array.length <= 1) {
            return; // 如果数组为空或只有一个元素,无需排序
        }
        int[] temp = new int[array.length]; // 创建一个临时数组来辅助排序
        mergeSort(array, 0, array.length - 1, temp); // 调用归并排序函数
    }

    private static void mergeSort(int[] array, int left, int right, int[] temp) {
        //直到每个子数组只包含一个元素 即:left == right
        //也就是说最小排序单位是长度为2的数组,当长度为2时,此时无法再递归,而进行排序
        if (left < right) {
            int mid = (left + right) / 2;
            // 对左半部分进行归并排序
            mergeSort(array, left, mid, temp);
            // 对右半部分进行归并排序
            mergeSort(array, mid + 1, right, temp);
            // 合并两个有序数组
            merge(array, left, mid, right, temp);
        }
    }

    private static void merge(int[] array, int left, int mid, int right, int[] temp) {
        int i = left; // 左数组的起始索引
        int j = mid + 1; // 右数组的起始索引
        int k = left; // 临时数组的起始索引

        // 比较左右两个数组的元素,将较小的元素放入临时数组
        while (i <= mid && j <= right) {
            if (array[i] <= array[j]) {
                temp[k++] = array[i++];
            } else {
                temp[k++] = array[j++];
            }
        }

        // 将左数组剩余部分复制到临时数组
        while (i <= mid) {
            temp[k++] = array[i++];
        }

        // 将右数组剩余部分复制到临时数组
        while (j <= right) {
            temp[k++] = array[j++];
        }

        // 将临时数组的元素复制回原数组
        for (i = left; i <= right; i++) {
            array[i] = temp[i];
        }
    }

算法复杂度

时间复杂度(最坏)时间复杂度(最好)时间复杂度(平均)空间复杂度稳定性
O(n log n)O(n log n)O(n log n)O(n)稳定
  • 平均时间复杂度为 O(n log n),其中 n 是待排序数组的大小。

  • 最好情况和最坏情况下的时间复杂度也都是 O(n log n)。

  • 归并排序需要额外的空间来存储临时数组,在最坏情况下需要与输入数组一样大的空间,因此空间复杂度是 O(n)。

归并排序的优化方式

上述代码实现了基本的归并排序算法,但它在合并过程中存在一个潜在的优化点。在归并排序的合并阶段,将左右两个有序数组合并为一个有序数组时,可以通过判断左边数组的最大值和右边数组的最小值来优化合并操作,避免不必要的比较。

这个优化点可以通过添加一个条件来判断是否需要合并,如果左边数组的最大值小于等于右边数组的最小值,则无需合并,因为两个数组已经是有序的,不需要进行额外的比较和移动。

private static void merge(int[] array, int left, int mid, int right, int[] temp) {
    int i = left; // 左数组的起始索引
    int j = mid + 1; // 右数组的起始索引
    int k = left; // 临时数组的起始索引

    // 如果左数组的最大值小于等于右数组的最小值,则无需合并,直接返回
    if (array[mid] <= array[j]) {
        return;
    }

    // 比较左右两个数组的元素,将较小的元素放入临时数组
    while (i <= mid && j <= right) {
        if (array[i] <= array[j]) {
            temp[k++] = array[i++];
        } else {
            temp[k++] = array[j++];
        }
    }

    // 将左数组剩余部分复制到临时数组
    while (i <= mid) {
        temp[k++] = array[i++];
    }

    // 将右数组剩余部分复制到临时数组
    while (j <= right) {
        temp[k++] = array[j++];
    }

    // 将临时数组的元素复制回原数组
    for (i = left; i <= right; i++) {
        array[i] = temp[i];
    }
}

这个优化逻辑在判断左右两个子数组已经有序时,可以避免不必要的比较和赋值操作,提升归并排序的性能。

归并排序核心就是 递归,分治,记住这两个词就能大致理解这个算法,递归思想真的很重要,不容易完全理解,还得继续练习-_-


相关概念
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

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

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

相关文章

二百一十二、Flume——Flume实时采集Linux中的目录文件写入到HDFS中(亲测、附截图)

一、目的 在实现Flume实时采集Linux中的Hive日志写入到HDFS后&#xff0c;再做一个测试&#xff0c;用Flume实时采集Linux中的目录文件&#xff0c;即使用 Flume 监听Linux整个目录的文件&#xff0c;并上传至 HDFS中 二、前期准备 &#xff08;一&#xff09;安装好Hadoop、…

Linux使用root用户安装完MySQL软件后,配置MySQL这个普通用户登录

在 Linux 系统中&#xff0c;当您使用 root 用户安装 MySQL 后&#xff0c;系统会自动创建一个名为 mysql 的系统用户。这个 mysql 用户主要用于管理 MySQL 服务的运行&#xff0c;通常是没有登录系统的权限的。如果您希望使这个 mysql 用户能够登录到系统&#xff0c;您需要设…

JAVAEE初阶相关内容第十八弹--网络原理之TCP_IP【续集】

写在前 上一篇博客的重点内容主要讲了关于传输层的TCP协议、UDP协议。 点击跳转上一篇博客 重点介绍了协议的特点、协议端格式、需要重点理解并掌握TCP的工作机制&#xff08;十条&#xff09;。 TCP与UDP对比&#xff1f; TCP用于可靠传输的情况&#xff0c;应用于文件传输&am…

超完整的mysql安装配置方法(包含idea和navicat连接mysql,并实现建表)

mysql安装配置方法 1、下载mysql2、解压到指定的安装目录3、配置初始化文件my.ini4、配置用户变量和系统变量5、初始化mysql6、安装mysql服务并启动修改密码7、使用idea连接mysql8、使用Navicat可视化工具连接mysql&#xff0c;并实现新建数据库&#xff0c;新建表 1、下载mysq…

【数据结构与算法篇】八种排序 (C++实现)

多种排序算法的Cpp实现 一. 排序的概念及其运用排序的概念 二. 一图速览常见排序三. 排序的C实现1> 直接插入排序2> 希尔排序希尔排序代码实现(希尔所实现)希尔排序代码实现(优化版) 3> 选择排序选择排序的代码实现(同时选出最大和最小的元素) 4> 堆排序堆排序的代…

CSS面经(未完待续)

1. CSS选择器及其优先级 !important > 行内样式 > id选择器 > 类/伪类/属性选择器 > 标签/伪元素选择器 > 子/后台选择器 > *通配符 2. 重排和重绘是什么&#xff1f;浏览器的渲染机制是什么&#xff1f; 重排(回流)&#xff1a;当增加或删除dom节点&…

面试就是这么简单,offer拿到手软(三)—— 常见中间件框架面试题,es,redis,dubbo,zookeeper kafka 等

面试就是这么简单&#xff0c;offer拿到手软&#xff08;一&#xff09;—— 常见非技术问题回答思路 面试就是这么简单&#xff0c;offer拿到手软&#xff08;二&#xff09;—— 常见65道非技术面试问题 面试就是这么简单&#xff0c;offer拿到手软&#xff08;三&#xff…

DAPP开发【09】NFT交易市场开发(hardhat测试)

测试文件下新建market.js文件 扁平化&#xff0c;将所有依赖放在tmp.sol&#xff0c;可以去给他人使用 npx hardhat flatten > tmp.sol 测试文件 const {expect} require(chai); const {ethers} require(hardhat);describe(Market,async function(){//定义三个合约&a…

long转int类型转换问题

在业务代码中排序时需要根据日期排序&#xff0c;写了如下代码 sorted((o1, o2) -> {String str1 null;String str2 null;try {Field field getField(fieldMap, configBO.getCodeName());str1 String.valueOf(field.get(o1));str2 String.valueOf(field.get(o2));} ca…

kubectl获取命名空间下所有configmap集合的方法

前言&#xff1a; 获取单个configmap并忽略特定字段的操作可参照&#xff1a;kubectl获取ConfigMap导出YAML时如何忽略某些字段。 要获取命名空间下所有ConfigMap并忽略特定字段&#xff0c;你可以使用kubectl命令与例如yq这样的工具结合使用来忽略或删除不需要的字段。以下是…

分布式ID服务实践

背景 分布式场景下需要一个全局 ID 来标识唯一性&#xff0c;比如在单数据库时通过表唯一主键即可实现唯一 ID&#xff0c;分库分表时就需要全局唯一 ID。 业务对唯一 ID 的要求如下&#xff1a; 全局唯一性 不能出现重复的 ID 号&#xff0c;既然是唯一标识&#xff0c;这…

CGAL的周期三角剖分(相关信息较少)

CGAL的周期二维三角剖分类旨在表示二维平面上的一组点的三角剖分。该三角剖分形成其计算空间的分区。它是一个单纯复体&#xff0c;即它包含任何k-单纯形的所有关联j-单纯形&#xff08;j<k&#xff09;&#xff0c;并且两个k-单纯形要么不重叠&#xff0c;要么共享一个公共…

数据结构与算法-D2D3线性表之顺序表

线性表&#xff1a;包含若干数据元素的一个线性序列&#xff0c;特征如下&#xff1a; 1&#xff09;对非空表&#xff0c;a0是表头&#xff0c;无前驱&#xff1b; 2&#xff09;an-1是表尾&#xff0c;无后继&#xff1b; 3&#xff09;其他元素仅且仅有一个前驱&#xff0c;…

Day02 Liunx高级程序设计2-文件IO

系统调用 概念 是操作系统提供给用户使其可以操作内核提供服务的一组函数接口 用户态和内核态 其中 ring 0 权限最高&#xff0c;可以使用所有 CPU 指令&#xff0c; ring 3 权限最低&#xff0c;仅能使用 常规 CPU 指令&#xff0c;这个级别的权限不能使用访问硬件资…

解决 vite 中 import.meta.globEager is not function 的问题

本人正在重构两年前搭建到一半的博客网站&#xff0c;相关依赖都很陈旧&#xff0c;用到了 npm-check-updates 检测项目可升级依赖&#xff1a; 升级完成后解决完依赖发现控制台报错 import.meta.globEager is not function解决方案&#xff1a; vite版本降至4.3.0 亲测有效&…

Shell数组函数:数组(二)

关联数组 注意&#xff1a;先声明关联数组 一、定义关联数组 方法一 #一次赋一值 #数组名[索引]变量值 [rootlocalhost ~]# declare -A ass_array1 [rootlocalhost ~]# ass_array1[index1]pear [rootlocalhost ~]# ass_array1[index2]apple [rootlocalhost ~]# ass_array1[ind…

gpt阅读论文利器

1. txyz.ai 读论文 严伯钧 3. consensus 两亿科学论文的资源库. 用英文. 中国经济发展, 美国加州没有,减肥没有. 2. chrome插件 gpt sidebar 3. gpt academic 论文润色和学术翻译 ,一键输出公式. 英语口语8000句. 托福备考计划表. 百词斩托福. 薄荷外刊. 分区笔记精读法.…

Container容器技术简介

本文介绍了容器技术出现背景&#xff0c;docker技术与容器编排技术的简单说明 背景 在传统项目的生产环境中&#xff0c;迁移一个用户态进程往往非常麻烦&#xff0c;因为一个用户态进程背后会附带这非常多例如函数库、中间件等的依赖项&#xff0c;但又没有像apt和yum一样的…

广州华锐互动:钢铁厂烫伤事故3D模拟急救演练提高企业应对突发事故能力

随着工业化进程的加快&#xff0c;钢铁行业作为国家经济的重要支柱产业&#xff0c;其安全生产问题日益受到社会各界的关注。近年来&#xff0c;我国钢铁企业不断加大安全生产投入&#xff0c;但事故频发的现象仍然时有发生。为了提高钢铁企业应对突发事故的能力&#xff0c;确…

如何使用llm 制作多模态

首先将任何非字符的序列信息使用特殊n个token 编码。 具体编码方法以图像为例子说明&#xff1a; 将固定尺寸图像如256256 的图像分割为1616 的子图像块。 将已知的所有图像数据都分割后进行str将其看做是一个长的字符&#xff0c;而后去重后方式一个词表。 使用特殊1024 个tok…