归并排序(Merge Sort)

什么是归并排序

        归并排序(Merge Sort)是一种经典的排序算法,它采用分治法(Divide and Conquer)策略,将一个大数组分为两个小数组,分别进行排序,然后将这两个已排序的小数组合并成一个有序的大数组。归并排序在最坏情况下的时间复杂度为 O(nlog⁡n)O(nlogn),并且是一种稳定的排序算法。

核心思想

归并排序的核心思想可以分为以下几个步骤:

  1. 分解(Divide):将待排序的数组分为两个子数组,直到每个子数组只包含一个元素。因为一个元素的数组是有序的。
  2. 解决(Conquer):递归地对这两个子数组进行归并排序。
  3. 合并(Merge):将两个已排序的子数组合并成一个有序的数组。

快速排序与归并排序的比较

        快速排序和归并排序都是经典的排序算法,均采用分治法(Divide and Conquer)的思想。尽管它们在基本原理上相似,但在实现细节、性能特征及适用场景上存在显著差异。

1. 基本思路

  • 快速排序
    • 选择一个基准元素(Pivot),通常是数组的第一个元素或随机选择。
    • 将数组分为两个部分:左侧部分包含小于基准的元素,右侧部分包含大于基准的元素。
    • 递归地对这两个部分进行快速排序,直到整个数组有序。
  • 归并排序
    • 将数组递归分为两个子数组,直到每个子数组只包含一个元素。
    • 然后将这两个已排序的子数组合并成一个有序的数组。
    • 合并过程是关键,它需要额外的空间来存放临时数组。

2. 稳定性

  • 快速排序:不稳定排序。在排序过程中,相等元素的相对顺序可能会被改变。
  • 归并排序:稳定排序。相等元素的相对顺序在排序后保持不变,这在某些应用中非常重要。

 示例一

题目描述

代码实现

import java.util.Scanner;public class Main {final static int N = 1000000;private static int[] temp = new int[N];public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int arr[] = new int[n];for (int i = 0; i < n; i++) {arr[i] = scanner.nextInt();}mergeSort(arr, 0, n - 1);for (int i = 0; i < n; i++) {System.out.print(arr[i] + " ");}}public static void mergeSort(int[] arr, int l, int r) {if (l >= r) return;int mid = (l + r) >> 1;mergeSort(arr, l, mid);mergeSort(arr, mid + 1, r);int k = 0, i = l, j = mid + 1;while (i <= mid && j <= r) {if (arr[i] <= arr[j])temp[k++] = arr[i++];elsetemp[k++] = arr[j++];}//将剩余元素复制到temp数组中while (i <= mid) temp[k++] = arr[i++];while (j <= r) temp[k++] = arr[j++];//将temp数组中的元素复制回arr数组for (int t = l; t <= r; t++)arr[t] = temp[t - l];}
}

示例二

题目描述

求逆序对的数量

代码实现

import java.util.Scanner;public class Main {final static int N = 1000000;private static int[] temp = new int[N];public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int arr[] = new int[n];for (int i = 0; i < n; i++) {arr[i] = scanner.nextInt();}scanner.close();System.out.println(mergeSort(arr, 0, n - 1));}public static long mergeSort(int[] arr, int l, int r) {if (l >= r) return 0;int mid = (l + r) >> 1;long res =  mergeSort(arr, l, mid)+mergeSort(arr, mid + 1, r);int k = 0, i = l, j = mid + 1;while (i <= mid && j <= r) {if (arr[i] <= arr[j])temp[k++] = arr[i++];else{res += (mid - i + 1);temp[k++] = arr[j++];}}while (i <= mid)temp[k++] = arr[i++];while (j <= r)temp[k++] = arr[j++];for(i=l,j=0;i<=r;i++,j++)arr[i]=temp[j];return res;}
}

逆序对数量算法思路与实现

        求逆序对的算法是利用了归并排序的思想,在归并排序的过程中会将序列分为两部分,此时逆序对可以分为三种情况:两个数都在左边的(设为s1)。两个数都在右边的(s2),一个数在左边一个数在右边的(s3)。现在假设我们在归并排序的时候写的函数mergeSort(int[] arr, int l, int r)可以返回l到r区间中逆序对数量。那么s1=mergeSort(a, l, mid),s2=mergeSort(a, mid + 1, r);,s3很显然没有直观的答案。

        那么核心问题就在于怎么求s3,以及怎么使我们的mergeSort(int[] arr, int l, int r)可以返回l到r区间中逆序对数量。

        

        在归并排序中,左右两边都是排序好的数列。因此,对于右图中的B我们只需要找到左边第一个大于B的数A,那么A后面的数都是大于B的。假设A的下标为i,不难得到我们要统计的数目为:mid-i+1,else中的语句即代表左边数列中的数大于右边中的数了,我们可以在此时将mid-i+1加到总答案中去。那么s3的问题就解决了。经过递归之后,res 即我们所求。

时间复杂度

  • 最佳情况:O(nlog⁡n)O(nlogn)
  • 平均情况:O(nlog⁡n)O(nlogn)
  • 最坏情况:O(nlog⁡n)O(nlogn)

        归并排序在所有情况下的时间复杂度都是 O(nlog⁡n)O(nlogn),这使得它在处理大规模数据时非常高效。

空间复杂度 

归并排序的空间复杂度为 O(n)O(n),因为在合并过程中需要额外的存储空间来存放临时数组。

优点

  • 稳定排序,能够保持相等元素的相对顺序。
  • 在处理大规模数据时,性能稳定。

缺点

  • 需要额外的存储空间,不适合内存受限的环境。

应用场景

        归并排序广泛用于需要稳定排序的场合,如外部排序(处理无法完全放入内存的数据集)和链表排序。由于其稳定性和性能,归并排序在许多编程语言的标准库中被实现为排序算法。

总结

        归并排序是一种高效且稳定的排序算法,适用于大规模数据的排序。通过分治法的思想,归并排序能够在最坏情况下保持 O(nlog⁡n)O(nlogn) 的时间复杂度。虽然它需要额外的空间,但在许多实际应用中,归并排序仍然是一种非常有用的算法。

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

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

相关文章

音视频开发常见的开源项目汇总

FFmpeg 地址&#xff1a;https://ffmpeg.org/介绍&#xff1a;FFmpeg 是一个非常强大的开源多媒体框架&#xff0c;它可以用来处理视频和音频文件。它支持多种格式的转换、编码、解码、转码、流处理等。FFmpeg 包括了 libavformat、libavcodec、libavutil、libswscale、libpos…

组播 2024 9 11

PIM&#xff08;Protocol Independent Multicast&#xff09;是一种常用的组播路由协议&#xff0c;其独立于底层的单播路由协议&#xff0c;能够在多种网络环境中有效地实现多播路由功能。PIM主要有两种模式&#xff1a;PIM Sparse Mode (PIM-SM) 和 PIM Dense Mode (PIM-DM)&…

【C++题解】1580. 扫雷(mine)

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1580. 扫雷&#xff08;mine&#xff09; 类型&#xff1a;二维数组 题目描述&#xff1a; 扫雷游戏是一款十分经典的单机小游戏。在 n 行 m 列的雷区中有一些格子含有地雷&#x…

I/O 多路复用:`select`、`poll`、`epoll` 和 `kqueue` 的区别与示例

I/O 多路复用是指在一个线程内同时监控多个文件描述符&#xff08;File Descriptor, FD&#xff09;&#xff0c;以便高效地处理多个 I/O 事件。在 UNIX/Linux 和 BSD 系统中&#xff0c;select、poll、epoll、kqueue 都是实现 I/O 多路复用的系统调用。它们各有特点&#xff0…

el-input设置type=‘number‘和v-model.number的区别

el-input设置typenumber’与设置.number修饰符的区别 1. 设置type‘number’ 使用el-input时想收集数字类型的数据&#xff0c;我们首先会想到typenumber&#xff0c;设置完type为number时会限制我们输入的内容只能为数字&#xff0c;不能为字符/汉字等非数字类型的数值&…

性能诊断的方法(五):架构和业务诊断

关于性能诊断的方法&#xff0c;我们可以按照“问题现象—直接原因—问题根源”这样一个思路去归纳。我们先从问题的现象去入手&#xff0c;包括时间的分析、资源的分析和异常信息的分析。接下来再去分析产生问题现象的直接原因是什么&#xff0c;这里我们归纳了自上而下的资源…

外观模式详解:如何为复杂系统构建简洁的接口

&#x1f3af; 设计模式专栏&#xff0c;持续更新中 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 外观模式 外观模式&#xff08;Facade Pattern&#xff09;为子系统中的一组…

linux系统安装miniconda3

一、下载minconda3 下载地址&#xff1a;https://docs.conda.io/en/latest/miniconda.html 一般国内访问比较困难&#xff0c;可到清华软件镜像站 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 需要特别注意自己的下载版本和自己的…

初识爬虫2

requests学习&#xff1a; 小技巧&#xff0c;如果你用的也是pycharm&#xff0c;对于控制台输出页面因为数据很长一行&#xff0c;不方便进行查看&#xff0c; 可以让它自动换行&#xff1a; 1.requests文档阅读学习链接&#xff1a;快速上手 — Requests 2.18.1 文档 需掌…

【运维监控】Prometheus+grafana监控zookeeper运行情况

运维监控系列文章入口&#xff1a;【运维监控】系列文章汇总索引 文章目录 一、prometheus二、grafana三、prometheus集成grafana监控zookeeper1、修改zookeeper配置2、修改prometheus配置3、导入grafana模板4、验证 本示例通过zookeeper自带的监控信息暴露出来&#xff0c;然后…

基于imx6ull平台opencv的图像采集和显示屏LCD显示功能(带Qt界面)

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 验证功能一、概述 本文档是针对imx6ull平台opencv的图像采集和显示屏LCD显示功能,创建Qt工程,在工程里面通过点击按钮,实现opencv通过摄像头采集视频图像,将采集的视频图像送给显示屏LCD进…

LabVIEW编程快速提升的技术

在LabVIEW程序员的成长过程中&#xff0c;很多技术和概念看似简单、常用&#xff0c;但真正掌握并能熟练运用&#xff0c;往往需要踏踏实实的实践与积累。没有什么是能够一蹴而就的&#xff0c;唯有通过不断的专注与深入&#xff0c;才能获得显著的提升。要想在LabVIEW开发上取…

SSM框架学习(三、MyBatis实践:提高持久层数据处理效率)

目录 一、Mybatis简介 1.简介 2.持久层框架对比 3.快速入门&#xff08;基于Mybatis3方式&#xff09; 4.ibatis方式的实现和原理 5.ibatis与mybatis之间的关系 二、Mybatis基本使用 1.向 sql 语句传参 &#xff08;1&#xff09;mybatis日志输出配置 &#xff08;2&…

为什么矩阵特征值之和等于主对角线元素之和,特征值乘积等于行列式值

首先给出特征值和特征向量的定义。 设A是n阶矩阵&#xff0c;如果数λ和n维非零向量x使关系式 Axλx &#xff08;1&#xff09; 成…

微信小程序使用canvas画图保存图片到手机相册

微信小程序要实现使用canvas绘制一个图&#xff0c;然后保存到手机相册 **最终效果&#xff1a;**实现生成以下图片 一、初始化canvas // wxml页面设置canvas标签 <canvas style"width: {{windowW}}px; height: {{windowH}}px;" disable-scrolltrue canvas-id&…

C++(2)之Linux多线程服务端编程总结

C之Linux多线程服务端编程读书笔记 Author: Once Day Date: 2023年1月31日/2024年8月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践…

C++:类与对象

一、面向对象编程 (一) 面向过程vs面向对象 面向过程&#xff08;Procedural-Oriented-Programming&#xff0c; POP&#xff09;和面向对象&#xff08;Object-Oriented-Programming&#xff0c;OOP&#xff09;&#xff0c;是两种典型的编程范式&#xff0c;通常是作为划分编…

频带宽度固定,如何突破数据速率的瓶颈?

目录 目录 引言 信道 频带宽度 信噪比 信噪比的重要性 影响信噪比的因素 码元 码元的特点&#xff1a; 码元与比特的关系&#xff1a; 码元的作用&#xff1a; 码元的类型&#xff1a; Question 类比解释&#xff1a; 技术解释&#xff1a; 引言 在现代通信系统中…

OpenAI o1:AI领域的“草莓”革命,华人科学家贡献卓越

最近&#xff0c;科技界的热门明星“草莓”频繁出现在大家的视线中。9月11号&#xff0c;The Information报道称&#xff1a;OpenAI计划在未来两周内推出一款更智能、更昂贵、更谨慎的AI模型&#xff01;网友们对此消息持怀疑态度&#xff0c;认为类似消息屡见不鲜&#xff0c;…

centos8构建nginx1.27.1+BoringSSL+http3+lua+openresty

需要接入http3&#xff0c;索性最新的nginx在构建一波&#xff0c;趟一遍坑 准备工作 1.环境命令安装 yum install GeoIP -y yum install GeoIP-devel -y yum install libmaxminddb-devel -y yum install -y patch wget zlib zlib-devel lftp gcc gcc-c make openssl-devel p…