你知道怎么样排序才能做到多快好省?


小智最近迷上了计算机算法,今天过来给大家讲讲排序算法。


准备讲排序算法之前,我们还是要先回顾一下排序这个概念。


排序是一门古老的科学。排序问题,用数学的方式可以表达如下


问题输入:给定n个数,a1,  a2,  a3, ..., an
要求输出:给出n个数的排列,a1', a2', a3', ..., an',使得 a1≤ a2≤ a3≤ ... ≤ an'


从更形象的角度来说,排序就是一群人站成一列,高的站前面,矮的站在后面。

一个家庭的所有成员按身高排列的示意图(小智实在找不到图了,画一个示意-_-)


关于排序,有几个描述算法特征的词语:

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;

不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;

时间复杂度:一个算法执行所耗费的时间。

空间复杂度:运行完一个程序所需内存的大小。


ok,了解完排序的相关概念以后,我有一个新的问题了:如何评价一个算法?小智认为,可以用一个词语来形容一个优秀的算法:多快好省


什么是“多快好省”?我们从算法的角度来解析一下:


:能够可靠处理大规模的数据

:算法的时间复杂度要更低的

:算法实现要符合稳定性

:算法的空间复杂度要更低的


到底哪些算法能够符合“多快好省”这个目标呢?我们先看一下目前的算法分类:


基于比较的排序算法的特征总结

注:本文不讨论非基于比较算法,比如计数排序、桶排序和基数排序


“多”的角度:从数据量的处理性能来看,需要不受随机因素影响的算法。冒泡算法、插入排序、快速排序这三个算法最好情况和最坏情况相差了一个数量级,这种不可靠性可能影响数据处理的效率。

“快”的角度:从时间复杂度的角度来看,希尔排序、归并排序、快速排序、堆排序的算法复杂度比较好,均低于O(n2)。

“好”的角度:从稳定性的角度来看,归并排序、插入排序、冒泡排序是稳定的

“省”的角度:从空间复杂度的角度来看,冒泡排序、插入排序、希尔排序、堆排序,空间复杂度为O(1)。其中,值得注意的是,一般写法的归并排序,空间复杂度为O(n),经过优化以后,使用空间可以为O(1)。


大家都看出了我的倾向了吧?小智从“多快好省”这四个方面,分析发现,归并算法在这四个方面表现不错。这次小智决定跟大家探讨一下归并算法


为什么归并排序能够做到多快好省?


解答这个问题之前,我们先了解一下什么是归并排序。


归并算法(merge sort)是一个分治算法(divide and conquer algorithm),冯·诺依曼(John von Neumann )在1945年发明了这个算法。这个算法将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。


实际上,归并算法的算法步骤非常简单:

第1步:拆分,把长度为n的输入序列分成两个长度为n/2的子序列

第2步:递归调用,对这两个子序列分别使用归并排序

第3步:合并,将两个排序好的子序列合并成一个最终的排序序列


这几步,我们来分别地讲一下:


什么是拆分?将一个长序列拆分成两个子序列。这个属于分治法中的“分”,将大问题降解为小问题。


将长序列拆分成两个子序列示意图


如果n为奇数,不能被2整除,可以将n/2向上取整,这个对算法没有影响。


有一个特殊情况是需要注意的:当子序列长度为1时,已经没有办法进行拆分了,可以直接执行下一步。


什么是递归调用?实际上是重复调用归并排序的程序。我们假想一下,在最开始的情况下,执行完第1步,长序列被拆分为2个子序列。接着执行第二步时,直接对子序列进行归并排序,那么下一步则是继续拆分子序列。如此下去,直至将长序列拆分到无法分拆的情形。


我们以图来说明会更加清晰一点:


归并算法递归调用的分拆效果


经过不断地递归调用,要处理的子序列长度越来越短,最后直至子序列长度为1。


有一个特殊情况是需要注意的:当子序列长度为1时,已经不需要继续调用归并排序了,因此可以直接跳过递归调用这个步骤。


什么是合并?是将两个排好序的子序列,合成一个也是排好序的序列。这个合并算法也是比较简单。


合并过程可以表述为:对于两个子序列,X和Y,其中X和Y都是升序排列,以及知道X和Y每个数据在原来长序列的位置,分别为Xp和Yp,如何将X和Y合并成一个稳定的升序排列?


由于这两个子序列都是升序排列,因此我们同时遍历这两个子序列,从遍历的位置各拿出一个数字,哪个数字小就把它拿出来插入合并序列中。如果遇到拿出来的两个数字相等的情况,则比较原来序列的位置,哪个小就把它拿出来即可。


按照上述合并步骤,我们可以写出合并伪代码了:

i = 1, j = 1, l = 1

申请一个临时变量temp,用于储存合并后序列

如果 X[i] < Y[j]: temp[l] = X[i], l = l + 1, i = i + 1

如果 X[i] < Y[j]: temp[l] = Y[j], l = l + 1, j = j + 1

如果 X[i] = Y[j]:

    如果 Xp[i] < Yp[j]: temp[l] = X[i], l = l + 1, i = i + 1

    如果 Xp[i] > Yp[j]: temp[l] = Y[j], l = l + 1, j = j + 1


我将合并流程补充到整个归并算法的执行流程当中,以图的形式做了一个示例:


归并排序整体流程示意图


归并排序我们了解完了,我们可以开始回答为什么归并排序能够表现如此出色:


“多”的角度:归并排序为什么能保证可靠地处理数据?


归并排序的拆分的流程,跟数据的原始排序没有关系,无论是最坏情况还是最好情况,时间复杂度都是一致的,能够稳定处理大量数据。


“快”的角度:归并排序的时间复杂度为什么是 O(n log n)?


设归并排序所消耗的时间为T(n),进行归并排序的拆分操作以后,将原来的问题划分为原来规模的二分之一,每一个划分出来的问题将耗费时间是T(n/2),最后把这两部分有序的数组合并在一起所花的时间为O(n),因此:


T(n) = 2×T(n/2) + O(n)


而划分次数最多可以有logn次,因此累加起来可得T(n) = O(n log n)


“好”的角度:归并排序为什么能保证稳定呢?


归并排序将序列分割到最小,而后将序列进行合并。假如两个数字是相等的,在分割子序列的时候,不会更改他们之间的相对位置;在合并子序列的时候,只要能够做到先将位置在前的数字放到合并数组,这样就保证了排序结果的稳定。


“省”的角度:归并排序为什么空间复杂度可以为O(1)?


归并排序虽然原始写法的确是这样的,但是算法是可以改造的,我们只需要做到原地排序就可以了,而对于归并排序而言,原地排序的关键是合并。归并排序的原来写法,当进行合并时,是申请一个临时空间,将合并的数据放到临时空间当中,这个算法复杂度为 O(n) 


实际上,合并过程可以直接使用已有的位置。假设我们要合并的两段数组还是[13, 23, 37, 54]和[11,17, 26,29],在归并算法里,它们可以看成一个连续的数组形式:


[13, 23, 37, 54, 11, 17, 26, 29]


首先我们检查两段数组的第一个数,发现13 > 11,那么11这个数要拿出来,原来是要放到临时空间的第一个位置,但我们可以利用数组已有的位置,将11直接移到数组的第一个位置,然后原来第一段数组往后移一个位置,这样数组变为


[11, 13, 23, 37, 54, 17, 26, 29]


而后我们继续按照这种方式,合并[13, 23. 37. 54]和[17, 26 29]这两段数组。


可以发现,这种方式空间复杂度只需一个临时变量,用于协助数字移动,因此空间复杂度为O(1)。当然这样优化增加了移动的次数,为了空间,就要损失时间了。


总结一下,归并排序是个宝,一个多快好省的排序算法,大家如果遇到数据排序问题,可以优先考虑它。当然,归并排序并不是全能的,在某些类型问题下,有些算法比归并排序表现更为出色,往后小智会给大家解读。


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

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

相关文章

php 数据导出到excel文件,PHP导出数据到excel文件

下面介绍一个很另类的php导出数据到xls文件的方法&#xff0c;用到的函数有pack,iconv//上面三个自定义函数很重要&#xff0c;大家自行揣摩function xlsBOF() {echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);}function xlsEOF() {echo pack("ss"…

C# 使用 Index 和 Range 简化集合操作

C# 使用 Index 和 Range 简化集合操作Intro有的语言数组的索引值是支持负数的&#xff0c;表示从后向前索引&#xff0c;比如&#xff1a;arr[-1]从 C# 8 开始&#xff0c;C# 支持了数组的反向 Index&#xff0c;和 Range 操作&#xff0c;反向 Index 类似于其他语言中的负索引…

python 服务端框架_GitHub - edisonlz/fastor: Python服务端开发框架-极易上手,超出你的想象!...

欢迎使用Python 服务端开发框架 FastorFastor是一款专为Python 打造的API与后端管理系统&#xff0c;通过精心的设计与技术实现&#xff0c;集成了大部分稳定开发组件&#xff0c;memcache &#xff0c; redis&#xff0c;tornado&#xff0c;django&#xff0c;mysql 等。特点…

我的小服务器

朋友做了一个工控机的板子&#xff0c;我要了一块来&#xff0c;自己加上了迅驰1.2G CPU&#xff0c;再从笔记本上拆了一个1G内存和老的移动硬盘 30G IDE&#xff0c;就算搭起了一个最简陋的服务器。此外我从破DVD光驱上拆了一块铁皮底板&#xff0c;打了几个洞&#xff0c;把主…

爱心助农|百万斤丑苹果紧急待售!谁能帮这些特困孩子熬过寒冷冬天?

题记&#xff1a;人们在猛兽横行的蛮荒年代&#xff0c;得以从树上回归地面&#xff0c;是人们守望相助的结果&#xff0c;也是人类能繁衍至今的原因在这个什么都讲究颜值的年代有这样一个东西却以“丑”、“但非常好吃”引起了我们的注意它便是山西临猗的冰糖心丑苹果还要一个…

php云点播源码,乐视云直播 点播服务端api

php代码<?php class LeshiController{public $userid ;//用户idpublic $secret ;//私钥public $user_unique ;//用户唯一标识码&#xff0c;由乐视网统一分配并提供 UUIDpublic $zhibo_apiurl http://api.open.letvcloud.com/live/execute;//直播接口地址public $dianb…

微软开源AI诊断工具Error Analysis

喜欢就关注我们吧&#xff01;Error Analysis 使用机器学习技术&#xff0c;助数据科学家更好地了解模型错误模式。在 2020 年 5 月的微软 Build 大会上&#xff0c;微软推出了三个响应式的 AI&#xff08;Responsible AI&#xff0c;RAI&#xff09;工具包&#xff0c;这三个工…

【SDL的编程】VC环境搭建

SDL&#xff08;simple DirectMedia Layer&#xff09;是一个可跨平台的开源库&#xff0c;最近由于自己的兴趣&#xff0c;就把它windosXP下的环境搭建了下。PC&#xff1a;Mircrosoft Windows XP Service Pack3Platform&#xff1a;Mircrosoft Visual C 6.0SourceCode&#x…

php正则匹配标点符号,php 正则匹配包含字母、数字以及下划线,且至少包含2种...

【scikit-learn】scikit-learn的线性回归模型&#xfeff;&#xfeff; 内容概要 怎样使用pandas读入数据 怎样使用seaborn进行数据的可视化 scikit-learn的线性回归模型和用法 线性回归模型的评估測度 特征选择的方法 作为有监督学习,分类问题是预 ...icon图标http://images20…

# 保持最外层获取焦点_大事件!沈阳爱尔白内障焕晶诊疗中心正式启用,两位PanOptix三焦点人工晶体植入患者清晰见证!...

近日&#xff0c;沈阳爱尔眼科医院大东院区白内障焕晶诊疗中心正式投入使用&#xff01;由沈阳爱尔眼科医院大东院区业务院长朱建勋领衔的白内障手术团队始终与国内外一流水准保持同步&#xff0c;开创性引进了爱尔康AcrySof IQ PanOptix 新一代三焦点人工晶状体。中心最先入住…

使用 Tye 辅助开发 k8s 应用竟如此简单(六)

续上篇&#xff0c;这篇我们来进一步探索 Tye 更多的使用方法。本篇我们将进一步研究 Tye 与分布式应用程序运行时 Dapr 如何碰撞出更精彩的火花。巧了&#xff0c;巧了&#xff0c;真是巧了 今天正值 dapr 1.0 发布的日子。如果你暂时还不了解什么是 dapr。那不如通过以下简短…

eval() php,js-eval编码,js-eval解码

实例例子 1在本例中&#xff0c;我们将在几个字符串上运用 eval()&#xff0c;并看看返回的结果&#xff1a;eval("x10;y20;document.write(x*y)")document.write(eval("22"))var x10 document.write(eval(x17))输出&#xff1a;200 4 27例子 2看一下在其他…

李阳疯狂英语300句

1.Absolutely.(用于答话&#xff09;是这样;当然是;正是如此;绝对如此。2.Absolutely impossible!绝对不可能的&#xff01;3.All I have to do is learn English. 我所要做的就是学英语。4.Are you free tomorrow?你明天有空吗?5.Are you married?你结婚了吗&#xff1f;6.…

python函数用于创建对象_Python-创建类并使用函数更改其对象值

有人建议我把这个重新贴出来以便更清楚。上完一节课&#xff0c;剩下的就不上这节课了。欢迎任何指导。我已经得出了这个问题的一部分&#xff0c;在那里我坚持要保持简短。我还附上了我的工作。在下面的工作中&#xff0c;我希望能够创建一个包含一个变量的类。我希望能够更改…

BeetleX.WebFamily针对Web SPA应用的改进

BeetleX.WebFamily1.0在集成vueelementaxios的基础上添加应用页、窗体布局和登陆验证等功能。通过以上功能开发Web SPA应用时只需要编写vue控件和配置菜单即可实现应用开发。使用创建一个.net控制台项目&#xff0c;然后通过Nuget引入BeetleX.WebFamily1.0组件&#xff0c;并在…

php acl rbac,建站常用的用户权限管理模型ACL和RBAC的区别

常用的权限管理模型ACL和RBAC的区别1.ACLACL是最早也是最基本的一种访问控制机制&#xff0c;它的原理非常简单&#xff1a;每一项资源&#xff0c;都配有一个列表&#xff0c;这个列表记录的就是哪些用户可以对这项资源执行CRUD中的那些操作。当系统试图访问这项资源时&#x…

测光

所谓测光其实就是指数码相机根据环境光线系统依靠特定的测量方式而给出的光圈/快门组合的方式。简单的说&#xff0c;也就是对被摄物体的受光情况进行测量。一般来说&#xff0c;测光主要是测定被拍摄对象反射到镜头中的光亮度然后在根据这一亮度给出一定的光圈快门速度组合。而…

python3怎么安装gmpy2_python2/3 模块gmpy2在linux下安装

&#xff01;&#xff01;&#xff01;首先建议在Windows下安装 因为很方便&#xff01;&#xff01;&#xff01;gmpy2是解密RSA时所用脚本的一个模块python下输入 import gmpy2 ,提示 Traceback (most recent call last): File"", line 1, in ImportError: No mod…

华为年终奖,小员工分百万!任正非:钱给多了,不是人才也变成了人才!

华为今年又提前发了巨额年终奖&#xff0c;并公布了新的奖金方案&#xff0c;23级奖金额有近百万&#xff0c;并且宣称“上不封顶、绝不拖欠”&#xff0c;一时间引起热议。任正非签发的内部文件&#xff1a;华为不搞按资排辈&#xff0c;只要做出突出贡献&#xff0c;在新方案…

Redis缓存穿透、缓存雪崩、缓存击穿好好说说

前言 Redis是目前非常流行的缓存数据库啦&#xff0c;其中一个主要作用就是为了避免大量请求直接打到数据库&#xff0c;以此来缓解数据库服务器压力&#xff1b;用上缓存难道就高枕无忧了吗&#xff1f;no,no,no&#xff0c;没有这么完美的技术&#xff0c; 缓存穿透、缓存雪崩…