排序算法介绍(五)归并排序

0. 简介

        归并排序(Merge Sort)是一种分治思想的应用,它将待排序的数组不断拆分成小数组,直到每个小数组只有一个元素,然后将小数组两两合并,直到最终得到有序的数组。


1. 归并排序的实现

归并排序的基本思想:

  1. 分解:将待排序的数组从中间分成两部分,递归地对左右两部分进行分解,直到每个小数组只有一个元素,这时可以认为每个小数组是有序的。
  2. 解决:将两个有序的小数组合并成一个有序的数组。可以使用双指针法,比较两个数组的元素大小,按照从小到大的顺序将元素放入新的数组中。
  3. 合并:递归地将左右两个有序的数组合并成一个更大的有序数组,直到最终得到整个有序数组。

归并排序过程演示:

d0c5b326b4cf4d8aa51c168548c8a4d7.gif


2. 归并排序时空间复杂度分析

归并排序的时间复杂度和空间复杂度分析如下:

  1. 时间复杂度:

    • 归并排序的时间复杂度是 O(n log n),其中 n 是待排序数组的长度。这是因为归并排序采用分治策略,每次将数组分成两半进行递归排序,然后再合并。在每一层递归中,需要对所有元素进行比较和移动,所以每层的时间复杂度是 O(n)。由于递归的深度是 log n,因此总的时间复杂度是 O(n log n)。
  2. 空间复杂度:

    • 归并排序的空间复杂度是 O(n)。这是因为在合并过程中,需要创建一个临时数组来存储两个有序子数组的元素。临时数组的大小与原数组相同,所以空间复杂度是 O(n)。需要注意的是,这个空间复杂度是额外的空间,不包括递归调用所使用的栈空间。如果考虑递归调用的栈空间,最坏情况下的空间复杂度将达到 O(n log n)。

综上所述,归并排序的时间复杂度是 O(n log n),空间复杂度是 O(n)。


3. 归并排序C语言代码

C代码实现:

#include <stdio.h>  
  
// 合并两个有序数组  
void merge(int arr[], int l, int m, int r) {  
    int i, j, k;  
    int n1 = m - l + 1;  
    int n2 = r - m;  
  
    // 创建临时数组  
    int L[n1], R[n2];  
  
    // 将数据拷贝到临时数组  
    for (i = 0; i < n1; i++)  
        L[i] = arr[l + i];  
    for (j = 0; j < n2; j++)  
        R[j] = arr[m + 1 + j];  
  
    // 合并临时数组到原数组  
    i = 0;  
    j = 0;  
    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++;  
    }  
}  
  
// 归并排序函数  
void mergeSort(int arr[], int l, int r) {  
    if (l < r) {  
        int m = l + (r - l) / 2; // 计算中间位置  
        mergeSort(arr, l, m); // 对左半部分递归排序  
        mergeSort(arr, m + 1, r); // 对右半部分递归排序  
        merge(arr, l, m, r); // 合并左右两部分  
    }  
}  
  
// 测试代码  
int main() {  
    int arr[] = { 12, 11, 13, 5, 6, 7 };  
    int arr_size = sizeof(arr) / sizeof(arr[0]);   
    mergeSort(arr, 0, arr_size - 1); // 对数组进行归并排序  
    printf("Sorted array:\n");  
    for (int i = 0; i < arr_size; i++) {  
        printf("%d ", arr[i]);  
    }  
    printf("\n");  
    return 0;  
}

代码解释:

merge 函数:

  1. int n1 = m - l + 1;int n2 = r - m;:这两行代码计算两个子数组的长度。n1 是左子数组的长度,n2 是右子数组的长度。
  2. int L[n1], R[n2];:我们创建两个临时数组 LR 来存储左子数组和右子数组的元素。
  3. 接下来的两个循环将左子数组和右子数组的元素拷贝到临时数组 LR 中。
  4. 然后,我们使用三个指针 i, j, 和 k 来合并这两个有序的子数组。指针 ij 分别指向临时数组 LR 的当前元素,而指针 k 指向原数组 arr 的当前位置。
  5. 在合并的过程中,我们比较 L[i]R[j] 的值,并将较小的元素放入原数组 arr 中。这个过程会一直持续到我们遍历完 LR 中的所有元素。
  6. 最后,我们将剩余的元素(如果有的话)从 LR 拷贝到原数组 arr 中。

mergeSort 函数:

  1. if (l < r) { ... }:这个条件用于判断数组是否至少包含两个元素。如果只有一个元素或没有元素,那么数组已经是有序的,不需要进一步排序。
  2. int m = l + (r - l) / 2;:这行代码计算数组的中间位置。我们通过将数组的长度 (r - l) 除以 2 并加上起始索引 l 来得到中间位置 m
  3. mergeSort(arr, l, m);mergeSort(arr, m + 1, r);:这两行代码递归地对左子数组和右子数组进行排序。递归的终止条件是子数组的长度为 1 或 0。
  4. merge(arr, l, m, r);:当左子数组和右子数组都被排序后,我们使用 merge 函数将它们合并成一个有序的数组。

4. 归并排序代码运行结果

代码运行结果:

c4f654be288f47748b7faa316b2ebf58.png

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端笔记(二):CSS 选择器与特性

CSS&#xff08;层叠样式表&#xff09;是一种样式表语言&#xff0c;用于描述HTML或XML文档的呈现方式。它定义了如何在屏幕、纸张或其他媒体上显示文档的样式、布局和外观。 里面的代码由 选择器 { } 组成 体验 CSS CSS 可以让我们界面变得更加美观&#xff0c;这是 CSS 的…

flutter使用动态路由传参的最小案例

flutter中使用动态路由传递参数的封装案例&#xff0c;子组件页面只需要接收arguments参数即可&#xff0c;参数是一个map&#xff0c;里面包含有所需要的参数&#xff0c;类似于json。在MaterialApp中配置onGenerateRoute&#xff0c;然后动态判断传递参数&#xff1a; route…

MobaXterm连接相关

其实最终解决的方法&#xff0c;还是&#xff0c;因为要远程连接的是个局域网ip&#xff0c;我所在的ip和要连接的这个不在同一个局域网内&#xff0c;需要实验室搭的VPN才行。 甚至&#xff0c;我连防火墙都没关&#xff0c;也可以连接 至于修改密码&#xff0c;passwd&#…

猜数字赢金币

充值金币后开始游戏&#xff0c;猜中奖励10金币退出&#xff0c;不中扣除1金币继续。 (笔记模板由python脚本于2023年12月03日 21:52:23创建&#xff0c;本篇笔记适合熟悉程序函数式编程&#xff0c;熟练掌握基本数据类型的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&…

RocketMQ-RocketMQ集群实践

搭建RocketMQ可视化管理服务 下载可视化客户端源码下载 | RocketMQ 这里只提供了源码&#xff0c;并没有提供直接运行的jar包。将源码下载下来后&#xff0c;需要解压并进入对应的目录&#xff0c;使用maven进行编译。(需要提前安装maven客户端) mvn clean package -Dmaven.t…

项目实战一-性能测试筑基

这里写目录标题 一、为什么程序会出现性能问题、性能问题是怎么出现的&#xff1f;二、功能测试和性能测试的区别是什么&#xff1f;三、核心性能指标1、用户角度核心a、响应时间&#xff1a;b、并发量 2、成本角度3、运维角度面试题、并发量和吞吐量得区别&#xff1f;a、吞吐…

一些后端测试的东西

后端测试都测试些什么 接口测试最小单元测试联调测试 接口测试 接口测试要素 可重复性 异常覆盖 环境一致 如何进行方便的接口测试 测试工具&#xff1a; idea-httpRequest &#xff0c; apifox , postman, jmeter 如何使用idea进行高效的接口测试 编写接口 启动项目直接…

AD生产BOM表时如何隐藏不需要的器件记录

在完成图纸设计号通常需要生产BOM表&#xff0c;以便采购等&#xff0c;如果不做一些操作&#xff0c;往往输出的BOM表中包含一些非需要采购的器件&#xff0c;如下图 这时就需要对原理图或者PCB图做一些处理&#xff0c;以原理图为例&#xff0c;在需要屏蔽的器件上双击&#…

【C语言】扫雷小游戏初学者版

成功的秘诀就是每天都比别人多努力一点。 今天给大家带来一款非常经典的小游戏——扫雷的实现和讲解 这里是目录 前言整体框架1.打印菜单2.创建二维数组3.初始化棋盘4.打印棋盘5.布置棋盘中的雷6.排查雷和统计雷总体代码test.cgame.cgame.h 进阶&#xff08;递归展开&#xff0…

力扣572:另一棵树的子树

力扣572&#xff1a;另一棵树的子树 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所…

SpringBootAdmin

SpringBootAdmin 文章目录 SpringBootAdmin创建SpringBootAdmin服务端创建SpringBootAdmin客户端启动应用 总结 github地址 https://github.com/codecentric/spring-boot-admin 可以查到所有的版本号 创建SpringBootAdmin服务端 创建springBoot项目的时候&#xff0c;在ops选项…

【Vue】Vue CLI 脚手架(Vue Command Line Interface)安装教程(通过npm)

前言 Vue CLI&#xff08;Vue Command Line Interface&#xff09;是一个基于Vue.js的官方脚手架工具&#xff0c;用于快速搭建和管理Vue.js项目。它提供了一套完整的开发工具和配置&#xff0c;包括项目初始化、开发服务器、热重载、构建和打包等功能。 Vue CLI使用了Webpac…

使用PCSS实现的实时阴影效果

PCSS的技术可以使得阴影呈现出近硬远软的效果&#xff0c;并且能够实时实现。 其核心理念是通过模拟光源的面积来产生更自然、更柔和的阴影边缘。 具体步骤&#xff1a; 1、生成shadowmap 2、在进行阴影的比较时候进行平均&#xff0c;并非之前的shadow map 或者之后完全的阴影…

laraval6.0 GatewayWorker 交互通信

laravel 6.0 GatewayWorker 通讯 开发前准备下载 GatewayWorker 及操作方式前端demo测试效果项目中安装GatewayClient 开发前准备 GatewayClient 官网&#xff1a;https://www.workerman.net/ 当前使用的是宝塔操作 下载 GatewayWorker 及操作方式 前端demo 测试效果 项目中安…

unordered_map与unordered_set的实现(含迭代器)

unordered_map与unordered_set的实现 文章目录 unordered_map与unordered_set的实现前言一、问题一HashTable.h 二、问题二&问题三1.封装时如何取出key2.不同类型key如何建立对应关系 三、问题四&问题五问题四问题五 四、实现代码MyUnorderedSet.hMyUnorderedMap.hHash…

文案二次创作软件,文案二次创作的软件

文案创作成为品牌传播和营销不可或缺的一环。对于许多从业者而言&#xff0c;文案创作常常是一项既耗时又耗力的工作。为了解决这一文案创作的难题&#xff0c;市场上涌现出了众多的智能文案生成工具。我们通过对这些工具的介绍和分析&#xff0c;希望能够为你提供一些在文案创…

Kafka 架构深度解析:生产者(Producer)和消费者(Consumer)

Apache Kafka 作为分布式流处理平台&#xff0c;其架构中的生产者和消费者是核心组件&#xff0c;负责实现高效的消息生产和消费。本文将深入剖析 Kafka 架构中生产者和消费者的工作原理、核心概念以及高级功能。 Kafka 生产者&#xff08;Producer&#xff09; 1 发送消息到…

【C++】异常处理 ⑦ ( 异常类的继承层次结构 | 抛出 / 捕获 多个类型异常对象 | 抛出子类异常对象 / 捕获并处理 父类异常对象 )

文章目录 一、抛出 / 捕获 多个类型异常对象1、抛出 / 捕获 多个类型异常对象2、操作弊端3、完整代码示例 二、异常类的继承层次结构1、抛出子类异常对象 / 捕获并处理 父类异常对象2、完整代码示例 - 抛出子类异常对象 / 捕获并处理 父类异常对象 自定义的 异常类 , 可能存在 …

prometheus部署及与grafana结合应用

一、prometheus 介绍 prometheus server 是 Prometheus组件中的核心部分&#xff0c;负责实现对监控数据的获取&#xff0c;存储以及查询。它会定期从静态配置的监控目标或者基于服务发现自动配置的自标中进行拉取数据&#xff0c;当新拉取到的数据大于配置的内存缓存区时&…

Python 入门

目录 编译性语言 解释型语言 Python语言介绍 Python安装 配置环境变量 Pycharm安装 Pycharm基本使用 编译性语言 解释型语言 计算机是不能理解高级语言的&#xff0c;更不能直接执行高级语言&#xff0c;它只能直接理解机器语言&#xff0c;所以使用任何高级语言编写的程…