八大排序方法以及详细比较

先放一张图来看一下时空复杂度和稳定性复杂性的比较

 

 

在辅助空间的比较:

线形排序、二路归并排序的辅助空间为O(n),其它排序的辅助空间为O(1);

 

根据时空复杂度也可得到如下结论

插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。

反而在这种情况下,快速排序反而慢了。

当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。

当n较大时,关键字元素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下。

宜用归并排序。

 

一 直接插入排序

 直接插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。

  

博主自己用python实现的插入排序

二 堆排序

现在对于堆排序来说,我们先要做的是,把待排序的一堆无序的数,整理成一个大根堆,或者小根堆,下面讨论以大根堆为例子。

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

  a.假设给定无序序列结构如下

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

此处必须注意,我们把6和9比较交换之后,必须考量9这个节点对于其子节点会不会产生任何影响?因为其是叶子节点,所以不加考虑;但是,一定要熟练这种思维,写代码的时候就比较容易理解为什么会出现一次非常重要的交换了。

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

在真正代码的实现中,这时候4和9交换过后,必须考虑9所在的这个节点位置,因为其上的值变了,必须判断对其的两个子节点是否造成了影响,这么说不合适,实际上就是判断其作为根节点的那棵子树,是否还满足大根堆的原则,每一次交换,都必须要循环把子树部分判别清楚。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

牢记上面说的规则,每次交换都要把改变了的那个节点所在的树重新判定一下,这里就用上了,4和9交换了,变动了的那棵子树就必须重新调整,一直调整到符合大根堆的规则为截。

此时,我们就将一个无序序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

这里,必须说明一下,所谓的交换,实际上就是把最大值从树里面拿掉了,剩下参与到排序的树,其实只有总结点的个数减去拿掉的节点个数了。所以图中用的是虚线。

b.重新调整结构,使其继续满足堆定义

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

 

博主自己用python实现的堆排序

三 冒泡排序

冒泡排序分从大到小和从小到大两种排序方式。它们的唯一区别就是两个数交换的条件不同,从大到小排序是前面的数比后面的小的时候交换,而从小到大排序是前面的数比后面的数大的时候交换。我这里只说 从小到大的排序方式。

冒泡排序的原理:从第一个数开始,依次往后比较,如果前面的数比后面的数大就交换,否则不作处理。这就类似烧开水时,壶底的水泡往上冒的过程。
 

现以数组[8,7,6,4,5]为例,我们通过将这个数组按从小到大的方式排序,来说明冒泡排序的过程。

第一次循环:此次循环的多次比较交换,使最大的数字8冒到最上面。
在这里插入图片描述

在这里插入图片描述

第二次循环:此次循环中的多次比较和交换,使7往上冒,最终排到倒数第二个位置。
在这里插入图片描述

在这里插入图片描述

你会发现,这次循环比前面少一次循环比较。

这是因为第一次循环时已经把最大的8排到最上面的位置了,这次排序肯定不会去占用最上面的位置的,所以此时比较次数可以比前面少一次。

第三次循环:同理,此时6会往上冒。比较次数同理又会比前面少一次。
在这里插入图片描述

在这里插入图片描述

此时看着最后的结果已经是从小到大了,这是因为在原始数组中,5就在4的上面。

但实际我们不知道5是在4的上面,我们就得继续完成最后一次循环比较。

第四次循环: 5已经排在4的上面了,比较后不交换。
在这里插入图片描述

在这里插入图片描述

博主自己用python实现的冒泡排序

四 希尔排序

 把待排序的数据元素分成若干个小组,对同一个小组内的数据元素用直接插入法排序;小组的个数逐次缩小,当完成了所有的数据元素都在同一个小组内的排序后,排序过程结束。希尔排序又称为缩小增量排序。初始数据的有序程度会影响希尔排序的时间,但是有序无序不会影响他的正确程度。
希尔排序的过程图:

总结:希尔排序就通过缩小增量使得,数据逐渐变得有序,直到增量d=1,使得数据变得完全有序。

博主自己用python实现的希尔排序

五 归并排序

核心思想:分治。

下面我们来看归并排序的思路(先讲思路再来具体讲归并的细节):

归并排序(Merge Sort)

当我们要排序这样一个数组的时候,归并排序法首先将这个数组分成一半。如图:

 

 

然后想办法把左边的数组给排序,右边的数组给排序,之后呢再将它们归并起来。当然了当我们对左边的数组和右边的素组进行排序的时候,再分别将左边的数组和右边的数组分成一半,然后对每一个部分先排序,再归并。如图:

 

对于上面的每一个部分呢,我们依然是先将他们分半,再归并,如图:

 

 

 

分到一定细度的时候,每一个部分就只有一个元素了,那么我们此时不用排序,对他们进行一次简单的归并就好了。如图:

 

 

归并到上一个层级之后继续归并,归并到更高的层级,如图:

直至最后归并完成。

 

归并细节:

比如有两个已经排序好的数组,如何将他归并成一个数组?

我们可以开辟一个临时数组来辅助我们的归并。也就是说他比我们插入排序也好,选择排序也好多使用了存储的空间,也就是说他需要o(n)的额外空间来完成这个排序。只不过现在计算机中时间的效率要比空间的效率重要的多。无论是内存也好还是硬盘也好可以存储的数据越来越多,所以设计一个算法,时间复杂度是要优先考虑的。

整体来讲我们要使用三个索引来在数组内进行追踪。

 

蓝色的箭头表示最终选择的位置,而红色的箭头表示两个数组当前要比较的元素,比如当前是2与1比较,1比2小,所以1放到蓝色的箭头中,蓝色的箭头后移,1的箭头后移。

 

然后2与4比较,2比4小那么2到蓝色的箭头中,蓝色箭头后移,2后移,继续比较.....

归并思路就是这样了,最后唯一需要注意的是那个先比较完的话,那么剩下的直接不需要比较,把后面的直接移上去就可以了,这个需要提前判定一下。
 

博主自己用python实现的归并排序

六 快速排序

快速排序是冒泡排序的改进版,也是最好的一种内排序,在很多面试题中都会出现,也是作为程序员必须掌握的一种排序方法。

思想:1.在待排序的元素任取一个元素作为基准(通常选第一个元素,但最的选择方法是从待排序元素中随机选取一个作为基准),称为基准元素;

       2.将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边;

       3.对左右两个分区重复以上步骤直到所有元素都是有序的。

所以我是把快速排序联想成东拆西补或西拆东补,一边拆一边补,直到所有元素达到有序状态。

下面再看看示图理解下吧:

                                  

 

                                  

6.对元素5两边的元素也重复以上操作,直到元素达到有序状态。

博主自己用python实现的快速排序方法

七 选择排序

1. 图示过程

 

说明:此图示为未优化的选择排序示意图,意在说明原理

2. 动图展示

 

说明:此动图为优化后的选择排序,每趟只交换一次

3. 文字叙述过程

  • 第1趟比较:拿第1个元素依次和它后面的每个元素进行比较,如果第1个元素大于后面某个元素,交换它们,经过第1趟比较,数组中最小的元素被选出,它被排在第一位
  • 第2趟比较:拿第2个元素依次和它后面的每个元素进行比较,如果第2个元素大于后面某个元素,交换它们,经过第2趟比较,数组中第2小的元素被选出,它被排在第二位
  • ......
  • 第n-1趟比较:第n-1个元素和第n个元素作比较,如果第n-1个元素大于第n个元素,交换它们

 

八 基数排序

 

原理

基数排序的思想其实挺有意思,举个栗子来说,有如下数组:

这里写图片描述

第一趟:

我们首先对这个数组按照其个位数进行分组,结果如下:
这里写图片描述
然后将分组后的数据按照索引的大小取出,得到新的数组如下:
这里写图片描述
第一趟排序后完成的工作为将数组按照个位数由小到大的顺序进行了排序
第二趟:

对第一趟的结果按照十位数进行分组,结果如下:
这里写图片描述
然后将分组后的数组按照索引的大小分别取出,得到新的数组如下:
这里写图片描述
第二趟排序后完成的工作是将数组按照十位数由小到大进行了排序,同时由于个位数已经进行了排序,因此将分组后的数组按照索引大小取出时,十位数相同的较小的会排在前面,如31和38,43和49
第三趟:

对第二趟的结果按照百位数进行分组,结果如下:
这里写图片描述
然后将分组后的数组按照索引的大小分别取出,得到新的数组如下:
这里写图片描述
第三趟排序后完成的工作是将数组按照百位数由小到大进行了排序,同时由于个位数和十位数都已经进行了排序,所以较小的数会排在前面

这样就完成了整个数组的排序.
由上面的过程可以知道如果最大数有n位,则需要n次(按照索引分组+按照索引大小取出数据)
两个小前提:

1.下面写的算法是对全为正整数的数组进行排序
2.该算法必须要先知道这个数组中最大值得位数,如999是3位

 

博主自己用python实现的基数排序
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值