探索归并排序:分而治之的排序艺术

news2024/9/27 0:37:08

1. 引言:排序算法的重要性与背景

排序是计算机科学中的基础问题之一,它在各种应用中都得到了广泛的应用,从搜索引擎到数据库管理系统。而归并排序(Merge Sort)作为一种经典的排序算法,通过分治法的思想,为我们提供了一种高效的排序策略。本文将带您深入了解归并排序的核心思想、步骤、复杂度以及实际应用。

(分治法介绍)icon-default.png?t=N6B9https://blog.csdn.net/qq_45467165/article/details/132453575?spm=1001.2014.3001.5501

2. 归并排序的核心思想

2.1 分而治之

归并排序的核心思想是分治法,它将待排序的数组分成两部分,分别对这两部分进行排序,然后将排好序的子数组合并起来,从而得到完全有序的数组。这种分而治之的策略使得归并排序能够高效地处理大规模数据。

3. 归并排序的步骤

归并排序是一种基于分治法的排序算法,通过将一个大的问题划分成小的子问题来实现排序。以下将详细描述归并排序的每个步骤。

3.1 分割数组

归并排序的第一步是将待排序的数组分割成两个子数组,这个过程称为分割。具体操作是选择数组的中间元素作为分割点,将数组一分为二。如果数组的长度是奇数,分割点会略微偏向左侧,确保分割后的两个子数组的大小差距最多为1。这个过程会一直进行下去,直到每个子数组的长度为1,即无法再分割为止。

3.2 递归排序

分割后,对每个子数组进行递归排序。递归排序的思想是将一个大问题分解为小问题,然后递归地解决这些小问题。因此,对于每个子数组,我们会再次应用归并排序算法。递归的终止条件是子数组的长度为1,因为长度为1的数组已经是有序的。

3.3 合并数组

递归排序将子数组排序好后,下一步是将这些排好序的子数组合并起来,构建一个完整的有序数组。这一步骤的核心在于比较子数组的元素,并按照从小到大的顺序逐个将它们放入一个临时数组中。具体操作包括:

  • 创建两个指针,分别指向待合并的两个子数组的开头。
  • 比较这两个指针所指向的元素,将较小的元素放入临时数组中,并移动指向该元素的指针。
  • 重复上述步骤,直到一个子数组的所有元素都放入了临时数组中。
  • 将另一个子数组中剩余的元素依次放入临时数组中。

合并完成后,临时数组中就存放了两个子数组合并后的有序结果。最后,将临时数组中的元素复制回原数组的对应位置,完成整个排序过程。

4. 归并排序的代码实现

4.1分步

4.1.1 分割数组

void mergeSort(vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;  // 计算中间索引
        mergeSort(arr, left, mid);             // 对左半部分递归排序
        mergeSort(arr, mid + 1, right);        // 对右半部分递归排序
        // 合并左右两部分的有序子数组
        merge(arr, left, mid, right);
    }
}

4.1.2 合并子数组

void merge(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;  // 左半部分子数组的长度
    int n2 = right - mid;     // 右半部分子数组的长度
    
    vector<int> leftArr(n1);  // 创建临时数组存储左半部分的元素
    vector<int> rightArr(n2); // 创建临时数组存储右半部分的元素

    // 将元素复制到临时数组中
    for (int i = 0; i < n1; i++) {
        leftArr[i] = arr[left + i];
    }
    for (int j = 0; j < n2; j++) {
        rightArr[j] = arr[mid + 1 + j];
    }

    int i = 0;
    int j = 0;
    int k = left;  // 从原数组的left位置开始填充

    // 逐个比较并合并元素
    while (i < n1 && j < n2) {
        if (leftArr[i] <= rightArr[j]) {
            arr[k] = leftArr[i];
            i++;
        } else {
            arr[k] = rightArr[j];
            j++;
        }
        k++;
    }

    // 将剩余的元素复制到数组中
    while (i < n1) {
        arr[k] = leftArr[i];
        i++;
        k++;
    }
    while (j < n2) {
        arr[k] = rightArr[j];
        j++;
        k++;
    }
}

4.2 示例代码

#include <iostream>
#include <vector>
using namespace std;

void merge(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;  // 左半部分子数组的长度
    int n2 = right - mid;     // 右半部分子数组的长度
    
    vector<int> leftArr(n1);  // 创建临时数组存储左半部分的元素
    vector<int> rightArr(n2); // 创建临时数组存储右半部分的元素

    // 将元素复制到临时数组中
    for (int i = 0; i < n1; i++) {
        leftArr[i] = arr[left + i];
    }
    for (int j = 0; j < n2; j++) {
        rightArr[j] = arr[mid + 1 + j];
    }

    int i = 0;
    int j = 0;
    int k = left;  // 从原数组的left位置开始填充

    // 逐个比较并合并元素
    while (i < n1 && j < n2) {
        if (leftArr[i] <= rightArr[j]) {
            arr[k] = leftArr[i];
            i++;
        } else {
            arr[k] = rightArr[j];
            j++;
        }
        k++;
    }

    // 将剩余的元素复制到数组中
    while (i < n1) {
        arr[k] = leftArr[i];
        i++;
        k++;
    }
    while (j < n2) {
        arr[k] = rightArr[j];
        j++;
        k++;
    }
}



void mergeSort(vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;  // 计算中间索引
        mergeSort(arr, left, mid);             // 对左半部分递归排序
        mergeSort(arr, mid + 1, right);        // 对右半部分递归排序
        // 合并左右两部分的有序子数组
        merge(arr, left, mid, right);
    }
}



int main() {
    vector<int> arr = {12, 11, 13, 5, 6, 7};
    int n = arr.size();
    mergeSort(arr, 0, n - 1);
    
    cout << "Sorted array: ";
    for (int num : arr) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

5. 归并排序的复杂度分析

5.1 时间复杂度

归并排序的时间复杂度为 O(n log n),其中 n 是待排序数组的长度。这个时间复杂度的分析可以从递归的角度来理解。在每一次递归中,都需要将待排序的数组分成两半,然后对这两半分别进行排序,最后再将排好序的子数组合并起来。假设每次合并操作的时间复杂度为 O(n),那么在进行 log n 层递归后,整个数组就会被完全排序。因此,归并排序的时间复杂度为 O(n log n)。

5.2 空间复杂度

归并排序的空间复杂度主要由临时数组的使用和递归调用的栈空间组成。

在合并子数组时,通常需要创建一个临时数组来存放合并后的结果。这个临时数组的大小与待排序数组的大小相同,因此空间复杂度为 O(n)。

在递归调用的过程中,每次递归都会消耗一定的栈空间。在最坏情况下,需要进行 log n 层递归,每层递归的栈空间为 O(1),所以总的空间复杂度为 O(log n)。

综合起来,归并排序的空间复杂度为 O(n + log n),但由于在大多数情况下 n 远大于 log n,所以通常将空间复杂度简化为 O(n)。

通过对时间复杂度和空间复杂度的分析,我们可以了解归并排序在不同情况下的性能表现。尽管归并排序的空间复杂度较高,但其稳定的时间复杂度使得它在实际应用中仍然具有重要价值。

6. 归并排序的应用与优势

归并排序在排序大规模数据时表现优异,它稳定、可预测,适用于各种数据类型。此外,归并排序还可以应用于外部排序,即数据量过大无法一次加载到内存中时。

7. 结论

归并排序作为一种高效稳定的排序算法,通过分治法的思想,为我们提供了一种优雅的排序策略。通过理解其核心思想和实现步骤,我们能更好地应对排序问题,并为解决其他复杂问题提供启示。未来,随着计算机科学的发展,归并排序或许会在更多领域发挥重要作用,为解决现实世界的复杂问题提供更多可能性。

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

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

相关文章

Python“牵手”京东工业商品详情数据采集方法,京东工业商数据API申请步骤说明

京东工业平台介绍 京东工业平台是京东集团旗下的一个B2B电商平台&#xff0c;主要面向企业客户提供一站式的采购服务。京东工业平台依托京东强大的供应链和配送能力&#xff0c;为企业用户提供全品类、全渠道、全场景的采购解决方案&#xff0c;涵盖电子元器件、机械配件、办公…

smartbi token回调获取登录凭证漏洞

2023年7月28日Smartbi官方修复了一处权限绕过漏洞。未经授权的攻击者可利用该漏洞&#xff0c;获取管理员token&#xff0c;完全接管管理员权限。 于是研究了下相关补丁并进行分析。 0x01分析结果 依据补丁分析&#xff0c;得到如下漏洞复现步骤 第一步&#xff0c;设置Engi…

java开源 VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城 小程序商城搭建 bbc

​ 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前…

FPGA功能及特点

集成电路芯片包括数字芯片和模拟芯片两大类&#xff0c;数字芯片又分为存储器芯片和逻辑芯片。 逻辑芯片一般包括CPU、GPU、DSP等通用处理器芯片以及专用集成电路芯片ASIC。 FPGA&#xff08;现场可编程门阵列&#xff09;就是逻辑芯片的一种。 FPGA功能 FPGA中文名是现场可…

【速成】蓝桥杯嵌入式省一教程:(十)利用共用体进行E2PROM读写

在上一节中我们了解到&#xff0c;AT24C02芯片&#xff08;E2PROM存储器&#xff09;内部含有256个8位字节&#xff0c;每一次只能对一个字节进行读写操作。因此&#xff0c;其只能处理unsigned char或uint8_t类型的数据&#xff0c;对于int、float型等大于占用一个字节的数据&…

VMware虚拟机---Ubuntu无法连接网络该怎么解决?

在学习使用Linux系统时&#xff0c;由于多数同学们的PC上多是Windows系统&#xff0c;故会选择使用VMware创建一个虚拟机来安装Linux系统进行学习。 安装完成之后&#xff0c;在使用时总是会遇到各种各样的问题。本片随笔就主要针对可能出现的网络问题进行一个总结&#xff0c;…

2022中国主要城市的绿地数据

绿地是城市生态的重要组成部分,在很多分析中都会用到绿地数据! 本次给大家带来的是中国主要城市的绿地数据!31个城市 该数据是中山大学的石茜等研究者通过深度学习方法,基于GoogleEarth影像和城市边界数据绘制的!数据格式为栅格格式(.tif)。 01 数据预览 我们以北京…

联邦学习:对“数据隐私保护”和“数据孤岛”困境的破局

作者&#xff1a;vivo 互联网安全团队- Tu Daxi 随着计算力、算法和数据量的巨大发展&#xff0c;人工智能迎来第3次发展高潮&#xff0c;开始了各行业的落地探索。然而&#xff0c;在“大数据”兴起的同时&#xff0c;更多行业应用领域中是“小数据”或者质量很差的数据。“数…

php开发websocket笔记(1)

1.运行server1.php文件 Windows命令行运行 php server1.php<?phperror_reporting(E_ALL); set_time_limit(0); //ob_implicit_flush(); $address 0.0.0.0;//可以监听网络上的请求 $address 127.0.0.1;//只能监听本机的请求$port 10005; //创建端口 $socket1 socket_cr…

智能型温湿度传感器在各个行业的广泛应用

在物联网时代&#xff0c;传感器的重要性是不言而喻&#xff0c;各类传感器在不同领域都有着极为重要的应用。如在智能家居领域&#xff0c;现代人们追求高品质舒适生活&#xff0c;对于所处环境要求进一步提升&#xff0c;以环境监测为主的智能家居设备广受追捧。这时候温湿度…

数据库——Redis 常见数据结构以及使用场景分析

文章目录 1. string2. list3. hash4. set5. sorted set 你可以自己本机安装 redis 或者通过 redis 官网提供的在线 redis 环境。 1. string 介绍 &#xff1a;string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的&#xff0c;但是 Redis 并没有使用 C 的字符串…

数据分析利器:pandas库的奥秘与代码示例

pandas是Python中一款强大的数据分析库&#xff0c;它提供了数据清洗、数据操作、数据可视化等功能&#xff0c;使得数据分析与处理变得更加高效和便捷。本文将从基本概念、基础知识、高级特性、实战案例和总结五个方面&#xff0c;深入介绍pandas库的用法和技巧。 一、基本概念…

安装Docker并配置镜像加速器、容器

1.安装docker服务&#xff0c;配置镜像加速器 安装软件包 [rootlocalhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 设置yum源 [rootlocalhost ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo…

ZDRE6VP4-1X/50MG24K4V比例压力阀放大器

DRE 6-11/100MG24K4M比、DRE 10-6X/200YMG24K4M、DRE 20-52/200YMG24K4M、DRE 20-6X/200YMG24K4M、ZDRE6VP1-1X/315MG24N9K4M、ZDRE6VP4-1X/50MG24K4V、Z3DRE6VP2-2X/50G24K4M、Z3DRE6VP1-2X/100G24K4M、Z3DRE10VP2-1X/100XYG24K4M、Z3DRE10VP1-1X/315XLG24K4V 功能: 设定值通…

【实践篇】DDD脚手架及编码规范 | 京东云技术团队

一、背景介绍 我们团队一直在持续推进业务系统的体系化治理工作&#xff0c;在这个过程中我们沉淀了自己的DDD脚手架项目。脚手架项目是体系化治理过程中比较重要的一环&#xff0c;它的作用有两点&#xff1a; &#xff08;1&#xff09;可以对新建的项目进行统一的规范&…

【校招VIP】java语言考点之类的加载过程

考点介绍&#xff1a; 类的加载过程在校招面试中是个高频考点。类只有在要运行的时候才会被加载进JVM,即编译后只有需要到这个类的时候才会把他加载进JVM运行&#xff0c;这种动态加载是依靠反射来实现的&#xff0c;一般来说一个class只会被加载一次...... 『java语言考点之…

oracle 启停操作

1. 监听端口启停 # 根据实际情况 切换至oracle用户 su - oracle# 状态查看 lsnrctl stat# 启动1521端口监听 lsnrctl start# 关闭1521监听 lsnrctl stop 2. 数据库服务启停 # 立即关闭服务 shutdown immediate# 启动服务 startup

前端需要理解的 JavaScript 知识

1 关于JavaScript JavaScript&#xff08;JS&#xff09;是单线程的、基于原型的、弱类型的、动态类型的、轻量的、支持面向对象/命令式/声明式编程的、头等函数的、多范式的、解释性&#xff08;直译式或即时编译&#xff09;的、也可在非浏览器环境下使用的动态脚本语言。Ja…

AI创作助手:介绍 TensorFlow 的基本概念和使用场景

目录 背景 环境测试 入门示例 背景 TensorFlow 是一个强大的开源框架&#xff0c;用于实现深度学习和人工智能模型。它最初由 Google 开发&#xff0c;现在已经成为广泛使用的机器学习框架之一。 TensorFlow 简单来说就是一个用于创建和运行机器学习模型的库。它的核心概念…

安装ssl证书有什么意义?

相信很多网站用户对ssl证书都有一定的了解&#xff0c;它就像是身份证&#xff0c;同时也能保障网站的安全&#xff0c;尤其是一些交易平台&#xff0c;不泄露客户的隐私。为了保证在线客户的隐私信息的安全&#xff0c;中小型企业一定要明白安装ssl证书的重要性。下面JoySSL的…