排序,也称为排序算法,可以说是我们学习算法的过程中遇到的第一个门槛,也是实际应用中使用得较为频繁的算法,我将自己对所学的排序算法进行一个归纳总结与分享,如有错误,欢迎指正!

(一)排序的分类

排序算法主要分为内部排序与外部排序,当数据量大时,数据无法全部加载到内存中,因此需要接抓外部存储(文件、磁盘等)进行排序。而内部排序则是指将要处理的所有数据加载到内部存储器中,并在内存中就完成排序。

本文针对的为内部排序。

(二)内部排序

2 交换排序

之前介绍了选择排序,而选择排序之所以叫选择排序就是因为是选择指定的元素放在指定的位置上。而这里我们介绍的交换排序自然就是通过交换元素来完成排序了(不是说其他排序方法不使用交换,而是核心思想是否是交换)。

本次介绍的两种交换排序算法,一种是学习排序第一步都会学的冒泡排序算法,另一种则是应用得比较多的快速排序算法。

2.1 冒泡排序(Bubble Sort)

冒泡排序(Bubble Sort),这是一个非常有意思的名字。我们看到这个名字不妨去想一下:

又一个大水缸,我们往水缸底部注射泡泡,泡泡就会一直往上冒。而冒泡排序就是越小的元素会经过不断交换,慢慢“冒”到数列的前端。

而冒泡排序是排序办法中的一种“笨”办法,之所以它“笨”,就是因为他在每一轮中都要将每两个相邻元素两两比较,逐步地将小的元素往数组前面“冒”,将较大的元素逐步往后“沉”。

不难想到,第n轮的比较交换结束后,倒数第n个元素就是排列好,有序的了。举第一轮的例子来说,当第一个和第二个元素相比较交换后,后者的元素是前两个元素里的最大值,那么第二个元素和第三个元素相比较交换后,第三个元素就是前三个元素里的最大值,那么一直比较交换到最后一个元素的时候,最后一个元素就是整个数组的最大值了。

那么我们可以借助这个特性,使得第二轮只排序交换第一个到第 n - 1 个元素,第三轮只排序交换第一个到第 n - 2 个元素,依此类推下去,最后一轮只需要排序第一个元素,也就是排序完成。

那么我们一共需要比较多少轮呢?由于我们不知道原数组的情况,最保险的做法是排序的轮数等于数组的元素数量减一,这样才能保证数组的每个元素都在应在的位置上,即是有序的。

但是假如我们实际上并不需要排列那么多次呢?举一个极端的例子,就是数组本身就是有序的,那么我们的比较就显得很多余了,这时我们可以做一个小小的优化,就是设定一个标志,标志本轮排序是否发生了交换,如果没有发生交换则说明数组已经有序,就可以中断排列了。

下面上张图来说明这个过程:橙色是排列好的有序数组段,图源网络

由于冒泡排序算法实在是入门级的排序算法,下面就不多赘述了,直接上代码:

冒泡排序是一个时间复杂度为O(n²)的稳定的算法,不管是否需要交换,都需要经过两两的比较。经过优化过的冒泡排序的最好情况是数组本身就是顺序的,最坏情况是数组逆序的(但是直接写个逆序就好了啊,为啥要用冒泡排序)。冒泡排序实际上应用得较少,但是是排序算法中最为基础的一种,还是需要理解和掌握的。

看起来交换排序很“笨”,但实际上并不是,接下来介绍的另一种交换排序方法却很快,应用得也十分频繁,那就是快速排序算法,也称为“快排”。

2.2 快速排序(Quick Sort)

快速排序,名字简单粗暴,而它之所以叫这个名字的原因就是因为其特性:快。

快速排序是冒泡排序的改进,冒泡排序的比较是比较“笨”的方法,要将一一去将两个相邻的元素比较。但快速排序的比较就十分的聪明了,这里会用到两个重要的思想,分而治之(Divide and conquer)和递归(recursive)的思想。

我们都学过二分法,对一个数组采用二分法一直分下去分到最底层就只剩一个或者两个元素,而对一个或两个元素的操作是很轻松的。那么我们想要排序一个数组,可以将一个数组利用一个数作为基准值(pivot)去分成两段,使得左边一段的数全比这个数小,右边一段的数全比这个数大,然后再分别将左边与右边再一次按照这种规则分段,一直到分最底层并将其排序。

这就是快速排序的思想,我们用一个实际生活中的例子来说明:

有十个高矮不一的男同学,现在需要我们去给他们从低个到高个排队。我们没办法去知道这十个同学身高的中值,于是我们随便挑了一个男同学作为基准(pivot),将比他个子低的男同学排到他的左边,将比他个子高的男同学排在他的右边(这个过程称为分区partition),这样这个作为pivot的男同学位置就排好了。

那么我们再分别在比他个子低的男同学里再随便找一个男同学作为基准(pivot),按照第一轮的方式排队,不难发现,作为基准的每个同学的位置都能排好,一直到作为我们第一个基准的男同学的左边的每一个同学的位置都排好为止。右边也同理。

当最后一个作为基准的同学队排完的时候,整个队列就排好了。而我们不断在分段中找基准的过程,就是递归。

快速排列的思想就是这样子了,并不算很难理解,快速排列借助的就是分而治之和递归的思想去完成的,但是在程序中应该如何实现呢?选择基准由于是随机的,很容易实现,但是快速排序的最大难点在于怎么将基准左右两端的元素正确的放在两端(也就是怎么实现分区)。

分区(partition)的实现思路

实现分区的原理就是不断将比基准值小的元素放在前面,比基准值大的数放在后面。

分区的实现方法有很多,这里我只介绍我常用的两种实现思路。

思路一:用一个数组实例来说明:

10,8,22,34,5,12,28,21,11

先选定第一个元素10作为pivot,相当于将10移除出数组作为基准值,然后在剩下的数组里找到比10小的元素就往前面放,比如先找到8,就将8放到10之后的第一位,在找到5放在10之后的第二位。当整个数组遍历完毕,将所有的比10小的数都尽可能往前放后:

10,8,5,34,22,12,28,21,11

此时我们在将基准值10与最后一个比10小的数交换:

5,8,10,34,22,12,28,21,11

这样以10为基准的分区就完成了,接下来就是向10的左边和右边分别递归的过程了。

思路二:用同样的一个数组实例来说明:

10,8,22,34,5,12,28,21,11

先选定第一个元素 10 作为pivot,那么我们就从第二个元素开始查找比10大的元素,一直查找到第三个元素22的时候,将22标记,然后在从第四个元素开始找比10小的元素,一直查找到第五个元素5,然后将22和5的位置交换。

10,8,5,34,22,12,28,21,11

再从5开始移动标记,移动到34,发现也比10大,标记34,再开始从22向后查找,查找到最后也没有比10大的数了,这时将基准值10放在标记的34的位置,这样第一轮的分区就完成了。接下来的就是向10的左边和10的右边分别递归分区过程。

在这查找比基准大的值的过程中,如果没有发现比基准值大的值,说明这个值就是在这个递归中的最大值,只需要将第一位和最后一位交换位置即可。

该思路坑太多,慎入!!!

再上一张图来说明整个排序过程:黄色为基准值pivot,橙色为已排列,图源网络

下面上根据思路一实现快速排序的代码:

思路二的代码我写了六十多行才实现,有想法的小伙伴可以尝试一下(如果三四十行实现了请务必要私信我),基准值的选取也可以不选择第一个,随机选择其他的值也是可行的(但是一定要找到结束分区的条件),快排的写法非常的多,大家也可以多试试其他的思路。

我们在开始介绍快速排序的时候,就提到快速排序的特点就是速度快。

快速排序的平均时间复杂度是O(nlogn),最坏情况达到O(n²)(顺序数列的快排,每次都会比较,实际上跟冒泡排序基本没有区别),那就有人问了,时间复杂度达到O(n²)的算法怎么能算快呢?但是实际上快速排序很少会出现最坏情况,在处理数据的时候一般都是O(nlogn),而且快排之所以快,是因为它在大多数情况会比其他O(nlogn)时间复杂度的算法表现的要更好。

因为快速排序的O(nlogn)中的隐含的常数因子通常很小,比复杂度稳定为O(nlogn)的归并排序要小很多,所以在遇到顺序性较弱的随机数列来说,它的性能通常要比归并排序要优秀很多。

最后总结一下,快速排序是冒泡排序算法的改进,本质上是在利用了分治思想的冒泡排序算法。冒泡算法是两两比较小的往前“冒”,大的往后“沉”;而快速排序则就是与基准值(pivot)比较小的往前“冒,”大的往后“沉”。

快速排序还有一个重点:分区。基本上掌握了分区(partition),就掌握了快速排序。

交换排序图解_排序算法学习分享(二)交换排序---冒泡排序与快速排序相关推荐

  1. 冒泡和快速排序的时间复杂度_排序算法学习分享(二)交换排序---冒泡排序与快速排序...

    排序,也称为排序算法,可以说是我们学习算法的过程中遇到的第一个门槛,也是实际应用中使用得较为频繁的算法,我将自己对所学的排序算法进行一个归纳总结与分享,如有错误,欢迎指正! 排序算法学习分享(一)选择 ...

  2. datatable的数据进行组内排序_排序算法学习分享(四)希尔排序

    排序,也称为排序算法,可以说是我们学习算法的过程中遇到的第一个门槛,也是实际应用中使用得较为频繁的算法,我将自己对所学的排序算法进行一个归纳总结与分享,如有错误,欢迎指正! 排序算法学习分享(一)选择 ...

  3. 交换排序图解_排序算法(一):初级比较排序

    排序算法,作为算法中最基础的一部分.其中很多思想值得我们学习借鉴,故有必要了解.掌握一些常见常用的排序算法.排序算法根据是否使用比较元素的思想,可分为两大类:比较排序.非比较排序.本文,我们将对比较排 ...

  4. 快速排序图解_排序算法

    1. 快速排序 分治思想 不稳定 时间复杂度:最差O(n^2),平均O(nlogn) 空间复杂度:O(n+1) 每次排序设置一个基准点,小于等于基准点的数放到基准点左边,大于等于基准点的数放到基准点右 ...

  5. 经典排序算法学习笔记二——快速排序

    快速排序 数据结构 不定 最差时间复杂度 O(n^2) 最优时间复杂度 O (n*log n) 平均时间复杂度 O (n*log n) 最差空间复杂度 根据实现的方式不同而不同 https://zh. ...

  6. C排序算法:(二)冒泡排序

    冒泡排序就是从左至右比较相邻的两个数值大小,如果右侧的数值较小,则交换两个数值的位置,较大的数值就会像泡泡一样一路向右漂浮. #include <stdio.h>//small to bi ...

  7. python版 常用排序算法 思路加详解 冒泡排序、快速排序、插入排序、选择排序

    注:这里所有排序操作都以从小到大排列为例,想要从大到小排的自行修改代码即可 目录 一.冒泡排序 思路: 步骤: 解析: 二.快速排序 思路: 步骤: 代码: 三.插入排序 思路: 代码: 四.选择排序 ...

  8. 算法学习(二)快速排序(上)

    快速排序采用的思想是分而治之. 1)将一整个大的无序序数组,根据数组中的某一个值,将数组分成两部分,一部分比这个值小,一部分比这个值大. 2)然后再对这两部分各自进行同样的操作,而当每一部分都有序之后 ...

  9. 精通八大排序算法系列:二、堆排序算法

    精通八大排序算法系列:二.堆排序算法 作者:July .二零一一年二月二十日 本文参考:Introduction To Algorithms,second edition. ------------- ...

最新文章

  1. python 输出大文本文件
  2. 一文搞懂负载均衡中的一致性哈希算法
  3. Windows下 VS2015编译boost1.62
  4. 计算机软件专利申请期限,软件发明专利申请期限为何那么长
  5. leetcode 6 --- convertZ
  6. 在Kubernetes上部署一个简单的、类PaaS的平台,原来这么容易!
  7. Linux: xclip,pbcopy,xsel用法 terminal 复制粘帖 (mac , ubuntu)
  8. ChannelMergerNode
  9. mysql 5.7 安装后添加root用户
  10. max转obj_工程动画制作 | Max插件Multiscatter进阶教程
  11. 我碰到的到现在为止,还没有找到比较好的解决方法的sps问题
  12. 学校计算机考试是win几,计算机一级考试考的是什么系统
  13. Excel 如何解决把数字格式变成会计格式的问题
  14. SEO内容构建流程,SEO内容建设策略有哪些?
  15. 路由器刷OPENWRT固件的方法
  16. 从“策略模式”聊聊“设计模式”有多重要?
  17. 数据可视化-echarts入门、常见图表案例、超详细配置解析及项目案例
  18. 时间复杂度和空间复杂度的概念及各种算法的时间复杂度 及举例
  19. python 修改pom文件_引用pom文件
  20. CSS基础(样式声明、引用(行内样式、内部样式、外部样式、导入样式)、样式优先级)

热门文章

  1. Azure上用API成功创建Lambda Function的截图
  2. SAP Analytics Cloud Smart Discovery不支持具有exception aggregation设置的模型
  3. SAP Business Application Studio和Authentication Trust Management
  4. 如何免费试用SAP的Fiori应用
  5. complete checkbox in Fiori
  6. 使用SAP Leonardo上的机器学习服务提取图片的特征向量
  7. SAP Kyma上都有哪些namespace?
  8. 隐藏search parameter在configuration tab里不相关的field
  9. Error message Exception raised without specific error
  10. 部署在云上的Fiori launchpad,其tile信息是从哪里取出来的