【算法】---归并排序(递归非递归实现)
参考
左程云算法
算法导论
前言
本篇介绍
- 归并排序
- 分治法
前置知识
- 了解递归, 了解数组。
引入
归并排序
归并排序最早是由公认的现代计算机之父John von Neumann发明的, 这是一种典型的分治思想
应用。
我们先介绍分治思想
分治思想
分治思想的想法基于递归, 许多算法可以通过自身调用, 来降低问题的规模, 又获得与原问题类似的子问题。可见, 分治法本质是递归思想的一个分支,分治法还要额外进行处理, 先进行递归地求解子问题, 然后合并这些子问题的解求出原问题的解。
通过一个简单的例子, 来讲解分治思想。
给定一个int类型的数组, 求解该数组的最大值。
你可能已经很熟悉了, 线性遍历即可
。
public static int getMaxValue1(int[] arr) {int n = arr.length;//max,初始默认为系统最小值。int max = Integer.MIN_VALUE;//无序数组, 直接遍历for(int i=0;i<n;i++) {max = Math.max(max, arr[i]);}return max;}
分治思想如何运用呢? 原数组区间范围在[0, arr.length - 1]
,求解原数组的大小可以被递归解决吗?
很有可能的想法, 直观上, 我们求解原数组的最大值就是求解在原数组序列中最大值。只需要等分序列即可, 将原数组序列的最大值,分解成左右子序列的最大值问题, 从递归上,对子序列可以同样这样处理,直到不需要递归继续降解规模了(递归模式结束, 处理基线条件,以防止死递归了)。
问题在于, 规模确实减小了,但左序列的最大值不一定是整个数组的最大值。 直觉上, 将左右序列的最大值进行比较,决定合并两个序列的最大值。
为此, 我们可以写出分治法的求解子问题的写法。
public static int getMaxValue(int[] arr) {if(arr==null||arr.length==0) {return Integer.MIN_VALUE;//处理值为null,传参为空数组的情况。}return process(arr, 0, arr.length-1);}public static int process(int[] arr, int l, int r) {if(l>r) {return Integer.MIN_VALUE;}if(l==r) {return arr[l];//直接返回值}//递归条件, 降低规模//分治的过程int mid = (l+r)/2;int lmax = process(arr, l, mid);int rmax = process(arr, mid+1, r);//合并的过程。return Math.max(lmax, rmax);}public static void main(String[] args) {int[] arr = {3,5,1,7,8,9,11,2};//对比两种方法, 观察结果是否一致。 相互验证!System.out.println("最大值:"+getMaxValue1(arr));System.out.println("最大值:"+ getMaxValue(arr));}
/*** output:* 最大值:11* 最大值:11*/
总结
对于每层递归:
分治法有三个步骤
- 分解原问题为若干子问题(不一定像上述例子二等分), 子问题是规模减小的原问题。
- 递归地处理这些子问题, 核心是什么时候继续递归分解, 什么时候直接求解。—写递归时必须想明白, 否则StackOverflow等着你。
- 合并处理子问题的解进而求出原问题的解。
能不能降低规模, 处理好递归,以及合并这个操作具体怎么写。写好分治法的难点。
归并排序
归并排序也是一种分治思想的体现。
基本思想就是,左边有序,右边有序。然后调整为整体有序。
- 分割: 将数组序列二等分, 利用递归不断分解。
- 合并: 合并两个有序序列,保持原来的元素的相对顺序不变。
结合代码和下面图片
public static void mergeSort(int[] arr) {//无效值null, 数组元素不为2,不需要排序。if(arr==null || arr.length<2) {return ;}//开始process(arr,0, arr.length-1);}public static void process(int[] arr, int l, int r) {//基线条件处理if(l>=r) {return ;}//选中分割下标, 直接取中值即可int mid = (l+r)/2;//左边递归调用process(arr,l, mid);//右边递归调用, 注意参数process(arr,mid+1,r);//合并操作merge(arr,l,mid,r);}public static void merge(int[] arr,int l,int m, int r) {int i = 0;int a = l;int b = m+1;//拷贝一个临时数组int[] help = new int[r-l+1];//比大小的过程while(a<=m && b<=r ) {help[i++]=arr[a]<=arr[b]?arr[a++]:arr[b++];}//处理剩余的序列while(a<=m) {help[i++] = arr[a++];}while(b<=r) {help[i++] = arr[b++];}//将数据拷贝回原序列。for(i=l;i<=r;i++) {arr[i] = help[i-l];}}//测试用例。public static void main(String[] args) {int[] arr= {4,1,6,23,8,9,11,0,2,3,4,4,4,4,10};System.out.println("排序前:"+Arrays.toString(arr));mergeSort(arr);System.out.println("排序后:"+Arrays.toString(arr));}/*** output:* 排序前:[4, 1, 6, 23, 8, 9, 11, 0, 2, 3, 4, 4, 4, 4, 10]* 排序后:[0, 1, 2, 3, 4, 4, 4, 4, 4, 6, 8, 9, 10, 11, 23]*/
开始有一个主方法,mergeSort
,只需要传递一个int数组即可。
process
这一函数是不断递归地分解问题。merge
函数是服务当前的process
函数。
比如,[4,8]
,[5,7]
给定两个子序列, 通过分离双指针和临时数组help
进行比较,原理是拷贝完较小的数组,然后拷贝完剩余的数组。
先将两个序列的比较结果拷贝进help数组,help=[4,5,7]
,此时还剩下一个元素8
,因为没有数可比了,序列[5,7]
的指针已经走到了尽头。只需要挨个检查将剩下的元素依次拷贝进help
数组即可。
以上就是对这行代码的解释:
//比大小的过程while(a<=m && b<=r ) {help[i++]=arr[a]<=arr[b]?arr[a++]:arr[b++];}//处理剩余的序列while(a<=m) {help[i++] = arr[a++];}while(b<=r) {help[i++] = arr[b++];}
最后, 将临时数组help
存储的有序序列依次拷贝回原数组的对应序列即可。注意这里是原数组进行修改。
//将数据拷贝回原序列。for(i=l;i<=r;i++) {arr[i] = help[i-l];}
递归版的复杂度
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn), 系统压栈高度为logn
,merge函数时间 O ( n ) O(n) O(n), 乘起来就是 O ( n l o g n ) O(nlogn) O(nlogn)。
空间复杂度: O ( n ) O(n) O(n), 借助了一个临时数组help
.
非递归实现
public static void mergeSort(int[] arr) {int n = arr.length;//step分组数, step为1,说明左右区间各有一个数(除非区间已经越界, 则相应调整)//先两两分组, 再以4个为一组, 8个为一组...直到单次分组已经超过数组总的元素个数就终止。for (int l, m, r, step = 1; step < n; step <<= 1) {l = 0;//后面就是讨论区间while (l < n) {//确定区间的中间下标m = l + step - 1;//判断右边界是否存在if (m + 1 >= n) {//无右侧, 不用后续merge了。break;//不存在说明单层的归并排序结束。}//求右边界r = Math.min(l + (step << 1) - 1, n - 1);//确定好了,l,m,r的值,进行合并merge(arr, l, m, r);//内层while循环进行调整l = r + 1;}}}public static void merge(int[] arr,int l, int m,int r) {int a = l;int b = m+1;int[] help = new int[r-l+1];int i = 0;while(a<=m && b<=r) {help[i++] = arr[a]<=arr[b]?arr[a++]:arr[b++];}while(a<=m) {help[i++] = arr[a++];}while(b<=r) {help[i++] = arr[b++];}for(i=l;i<=r;i++) {arr[i] = help[i-l];}}//测试用例。public static void main(String[] args) {int[] arr= {4,1,6,23,8,9,11,0,2,3,4,4,4,4,10};System.out.println("排序前:"+Arrays.toString(arr));mergeSort(arr);System.out.println("排序后:"+Arrays.toString(arr));}/*** output:* 排序前:[4, 1, 6, 23, 8, 9, 11, 0, 2, 3, 4, 4, 4, 4, 10]* 排序后:[0, 1, 2, 3, 4, 4, 4, 4, 4, 6, 8, 9, 10, 11, 23]*/
归并排序为什么如此高效, 左神说过是因为比较排序中的比较次数没有浪费。 确实如此, 比较排序可以抽象为决策树模型, 比较次数最少就是 n l o g n nlogn nlogn, 而对于三大平方的‘傻瓜式’排序算法, 因为浪费了比较次数,导致时间复杂度变高了。
练习
//请用递归和非递归方法实现。//阐述一下归并排序的思想。public static void mergeSort(int[] nums) {/*write code here! */}
你已经学会了归并排序了, 快速试试吧!。
总结
本篇并不涉及算法的严格分析, 因为算法导论一书中已经写好了严谨有力的证明(算法导论第二章和第4章)。
下次见!
相关文章:
【算法】---归并排序(递归非递归实现)
参考 左程云算法 算法导论 前言 本篇介绍 归并排序分治法 前置知识 了解递归, 了解数组。 引入 归并排序 归并排序最早是由公认的现代计算机之父John von Neumann发明的, 这是一种典型的分治思想应用。 我们先介绍分治思想 分治思想 分治思想的…...
UniVue大版本更新:UniVue2.0.0-preview
大版本发布说明 距离上次更新好像已经过去很久了,最近太忙了没时间维护新版本,也是自己在使用的过程中发现了很多问题也有了更多的灵感,由于和之前的版本区别太大,决定重新开一个大版本。这个UniVue2之后的版本追求是性能…...
RabbbitMQ篇(环境搭建 - 下载 安装)(持续更新迭代)
目录 一、Windows 1. 下载安装程序 2. 安装配置erlang 3. 安装rabbitMQ 4. 验证 二、Linux 1. 下载rpm包 1.1. 下载Erlang的rpm包 1.2. 下载socat的rpm包 1.3. 下载RabbitMQ的rpm包 2. 安装 2.1. 安装Erlang 2.2. 安装socat 2.3. 安装RabbitMQ 3. 启动RabbitMQ服…...
C++基础补充(02)C++其他控制语句break continue goto等
文章目录 1. break2. continue 语句3. goto 语句goto的存在 4. 跳出多重循环4.1 goto 直接跳转4.2 C11及其后版本的 return 语句4.3 使用标志变量 在C中,控制语句用于管理程序的执行流程。常见有 break、continue 和 goto。 1. break break语句主要用于在循环或者s…...
决策树中联合概率分布公式解释说明
学习决策树时书本中有一公式 7-3 是: P ( X x i , Y y j ) p i j ( i 1 , 2 , … , m , j 1 , 2 , … , n ) P(X x_i, Y y_j) p_{ij} \quad (i 1, 2, \dots, m, \ j 1, 2, \dots, n) P(Xxi,Yyj)pij(i1,2,…,m, j1,2,…,n) 这个公式表示的是随机变…...
计算机毕业设计 农场投入品运营管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...
php email功能实现:详细步骤与配置技巧?
php email发送功能详细教程?如何使用php email服务? 无论是用户注册、密码重置,还是订单确认,电子邮件都是与用户沟通的重要手段。AokSend将详细介绍如何实现php email功能,并提供一些配置技巧,帮助你更好…...
MapBox Android版开发 6 关于Logo
MapBox Android版开发 6 关于Logo Logo的显示查看源码及思路(Logo)第一步第二步 隐藏Logo示例查看源码及思路(Info)第一步第二步 隐藏Logo和Info示例 看到有网友留言问如何移除Logo,今天看了下V9源码,发现M…...
2024年房市
24年8月15日,国家统计局公布,“7月末,商品房待售面积73926万平方米”。(原文链接:https://www.stats.gov.cn/sj/zxfb/202408/t20240815_1955982.html) 7.39亿平方存量商品房,估价均价1万每平,总价约&am…...
index索引
index索引: create index 【1】on 【2】(【3】) 1为索引名,通常为id_表名_列名。2为表名。3为列名。 CREATE INDEX id_account_id ON account(id); -- 根据id创建索引 CREATE INDEX id_account_idname on account(id,name); -- 创建组合索引 索…...
理解互联网链路:从本地ISP到Tier 1 ISP运营商
1. 互联网服务提供商(ISP) 互联网服务提供商(ISP)是指提供互联网接入服务的公司或组织。它们负责将用户连接到互联网,并提供相关的服务,如电子邮件、网站托管和其他在线服务。ISP可以分为不同的层级&#…...
基于元神操作系统实现NTFS文件操作(三)
1. 背景 本文主要介绍DBR的读取和解析,并提供了基于元神操作系统的实现代码。由于解析DBR的目的是定位到NTFS磁盘分区的元文件$Root进行文件操作,所以只解析了少量的部分,其它部分可以参考相关文档进行理解。 DBR存在于磁盘分区的第一个扇区…...
深度学习与数学归纳法
最近发现,深度学习可以分为两个主要的阶段,分别是前向推理以及反向传播,分别对应着网络的推理和参数训练两个步骤。其中推理有时候也称为归纳推理。 在做参数训练的时候,本质上是在利用历史数据求网络参数的先验分布; …...
《Linux从小白到高手》理论篇(六):Linux软件安装一篇通
List item 本篇介绍Linux软件安装相关的操作命令,看完本文,有关Linux软件安装相关操作的常用命令你就掌握了99%了。 Linux软件安装 RPM RPM软件的安装、删除、更新只有root权限才能使用;查询功能任何用户都可以操作;如果普通用…...
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
1. 运行项目 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Appl…...
②EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器
EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器https://item.taobao.com/item.htm?ftt&id798036415719 EtherCAT 串口网关 EtherCAT 转 RS485 (接上一章) 自由协议通信步骤 (以MS-A2-1041为例) 接收与…...
matlab-对比两张图片的HSV分量的差值并形成直方图
%对比两张图片的HSV分量的差值并形成直方图,改个路径就能用,图片分辨率要一致 close all; clear all; clc; I1imread(E:\test\resources\image\1.jpg); I2imread(E:\test\resources\image\2.jpg); HSV1 rgb2ntsc(I1); HSV2 rgb2ntsc(I2); %HSV,HSV 代…...
微服务SpringGateway解析部署使用全流程
官网地址: Spring Cloud Gateway 目录 1、SpringGateway简介 1、什么是网关 2、为什么用网关【为了转发】 2、应用: 1.启动nacos 2.创建网关项目 3.网关配置1 4.网关配置2【了解】 5.过滤器配置【了解】 1、SpringGateway简介 核心功能有三个&…...
Solidity 存储和内存管理:深入理解与高效优化
在 Solidity 中,存储和内存管理是编写高效智能合约的关键组成部分。合约执行的每一步操作都可能涉及到数据的存储和读取,而这些操作对 gas 的消耗有很大影响。因此,理解 Solidity 的存储模型以及如何优化数据的管理对于合约的安全性、性能和成…...
机器学习篇-day02-KNN算法实现鸢尾花模型和手写数字识别模型
一. KNN简介 KNN思想 K-近邻算法(K Nearest Neighbor,简称KNN)。比如:根据你的“邻居”来推断出你的类别 KNN算法思想:如果一个样本在特征空间中的k 个最相似的样本中的大多数属于某一个类别,则该样本也属…...
【C++】STL--vector
1.vector的介绍 我们先来看看vector的文档介绍,实际中我们只要熟悉相关接口就好了。 成员函数 使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习 2 vector的使用 v…...
Java使用Redis的详细教程
Redis是一个基于内存的key-value结构数据库,即非关系型数据库,具有高性能、丰富的数据类型、持久化、高可用性和分布式等特点。在Java项目中,Redis通常用于缓存、分布式锁、计数器、消息队列和排行榜等场景。以下是在Java中使用Redis的详细教…...
严重 Zimbra RCE 漏洞遭大规模利用(CVE-2024-45519)
攻击者正在积极利用 CVE-2024-45519,这是一个严重的 Zimbra 漏洞,该漏洞允许他们在易受攻击的安装上执行任意命令。 Proofpoint 的威胁研究人员表示,攻击始于 9 月 28 日,几周前,Zimbra 开发人员发布了针对 CVE-2024-…...
php函数积累
对称函数 isset 判断数组arr中是否存在键key 返回值true/false isset(name,$arr) unset 删除数组中的键 需存在key不然抛出异常 unset($arr[name]) json_encode 数据转json格式 json_encode($arr) 一般形式 指定字符编码形式 json_decode json格式转原有数据格式 json_d…...
前端项目场景相关的面试题,包含验证码、图片存储、登录鉴权、动态路由、组件划分等项目场景实际的面试题
项目场景面试题 如何防止短信验证码被刷 问题场景 添加倒计时和图片滑动验证,避免不必要的资源浪费 发送短信验证码需要费用发送短信消耗服务器资源 公司的图片、视频、文件资源如何存储的 传统模式 分开存储到数据服务器,托管服务器到云端 缺点&…...
uniapp 上了原生的 echarts 图表插件了 兼容性还行
插件地址:echarts - DCloud 插件市场 兼容性这块儿不知道后期会不会支持其他浏览器 H5 的话建议可以用原生的不用这个插件...
共享单车轨迹数据分析:以厦门市共享单车数据为例(八)
副标题:基于POI数据的站点综合评价——以厦门市为例(三) 什么是优劣解距离法(TOPSIS)? 优劣解距离法(Technique for Order Preference by Similarity to Ideal Solution,简称TOPSI…...
sentinel原理源码分析系列(二)-动态规则和transport
本文是sentinel原理源码分析系列第二篇,分析两个组件,动态配置和transport 动态规则 Sentinel提供动态规则机制,依赖配置中心,如nacos,zookeeper,组件支持动态配置,模板类型为规则,支…...
ubuntu切换源方式记录(清华源、中科大源、阿里源)
文章目录 前言一、中科大源二、清华源三、阿里源 前言 记录ubunut切换各个源的方式。 备注:更换源之后使用sudo apt-get update更新索引。 提示:以下是本篇文章正文内容,下面案例可供参考 一、中科大源 地址:https://mirrors.u…...
【10】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-泛型基础全解(泛型函数、泛型接口、泛型类)及参数、接口补充
序言: 本文详细讲解了关于ArkTs语言中的泛型,其中包含泛型函数、泛型接口、泛型约束、泛型类及其中参数的使用方法,补充了一部分接口相关的知识,包括接口的继承和具体实现,也写到了一些边边角角的小知识,剩…...
网站开发背景怎么写/西安做网站公司
本节书摘来自异步社区出版社《相关性准则——大数据时代的高效能之道》一书中的第1章,第1.6节,作者:【意】Stefania Lucchetti,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.6 相关性准则 相关性准则——大数据时代的…...
做网站如何购买服务器吗/免费站长统计工具
目前市面上的产品帮助文档形式主要chm、word、pdf等,不过大家最常见的还是chm帮助文档。 小编这次要给大家安利一款制作出来效果远超chm帮助文档制作软件-Baklib。Baklib是一款专注于制作帮助文档软件,展示效果极佳,放在网页、产品内部有很好…...
做电商需要哪些网站/制作一个网站的全过程
>>回到总目录<< 为了不辜负已经订阅了专栏的同学们的信任,所以本专栏不会有任何的优惠活动。 另外,当订阅人数每次达到 2 n ( n > 2 ) 2^n(n>2) 2...
北京建设监理网站/关键词分析工具
创业者需具备的潜质——“变态”[温馨提示:变态需谨慎] 通常的情况下每个人都认为自己具有开创一翻事业的潜质,事实上真正具有这种潜质的人其实并不多。很多人只是简单的认为自己什么条件都不缺,唯一缺的就是钱。实际上即使他们有钱ÿ…...
完善政府门户网站建设方案/投百度做广告效果怎么样
01、简介 每个域控制器都有一个目录还原模式(DSRM)帐户,它的密码是在安装域控时设置的,实际上它对应的就是sam文件里的本地管理员“administrator”,基本很少会被重置,因此有着极强的隐蔽性。攻击者通过获取…...
黄石企业网站建设/网络营销的方式和方法
简介 自2007年发布以来,scikit-learn已经成为Python重要的机器学习库了。scikit-learn简称sklearn,支持包括分类、回归、降维和聚类四大机器学习算法。还包含了特征提取、数据处理和模型评估三大模块。 sklearn是Scipy的扩展,建立在NumPy和…...