排序算法的常用种类
初级排序
- 选择排序
- 插入排序
- 希尔排序
进阶排序
- 快速排序
- 归并排序
- (二叉堆排序)堆排序
非比较排序
-
计数排序
-
基数排序
-
桶排序
逆序对
逆序对,是分析排序算法的一个重要知识点。既然评估排序算法,就要知道如何去描述数组的排序程序。
逆序对,数组中两个逆序元素的对数。
比如【1, 5, 3】,其中(5, 3)就是逆序的,所以这个数组的逆序对为1。
那么一个数组的最好逆序对为0,最坏逆序对为 n(n-1)/2。
因为逆序对是两两进行组合,所以每个元素能够跟其他 n-1 元素配对,因为两两重复,所以除以2。
排序算法的基本思想(出现原因)
选择排序
选择数组中最小值,将其放入数组的最左边。
放入数组的方式,分为移动插入和一直交换相邻元素。
可以发现,每进行一次相邻交换,逆序对将会减一。
时间复杂度:每进行一次操作,需要比较(n-k)个元素,找到最小的元素。然后移动最小元素所在位置到前面。所以时间复杂度为n平方。
空间复杂度:无需其他空间。
插入排序
将数组分为排序和未排序两部分,每次从未排序部分选取一个元素,按顺序插入到已排序部分。
想象你打牌的时候,手上拿的是已排序的拍,每次抓牌的时候,都是把抓到的牌(未排序)按顺序插入到手上已排序的牌中。
插入的策略,也是交换相邻元素或者移动插入。每进行一个相邻交换,逆序对减一。
时间复杂度:选取未排序元素的时间为常数,所以需要确认将其插入到排序元素中的时间。(这是关键)之前说过,每交换相邻元素,逆序对会减一,所以插入到排序元素的时间跟逆序对成正比。
所以,可以很容易得出,最坏时间复杂度为(n-1)n/2,平均时间复杂度为(n-1)n/4,最好时间复杂度为1
希尔排序
希尔排序,间隔抽样子数组,对子数组完成排序。
为什么叫初级排序呢
(《算法》中将它们成为初级排序,也许是算法思想比较简单,时间复杂度比较高)
我们发现一个特点,虽然思想是不一样的,但实现中,基本上都是采用交换相邻元素的特点来达到插入的目的。而且每次交换相邻元素,都会使得逆序对减一。所以很自然,这些思想会和逆序对成正比。
快速排序
快速排序,是选取一个元素,然后经过交换元素,保证选定元素的左边都小于它,右边元素都大于它。每次操作后,选定元素的位置就是排序后的位置。
在实际算法中,我们可以看到,每交换元素的时候,逆序对减少的个数会大于等于1。这就是相较于初级算法的提升。
归并排序
归并排序,是分为多个子数组,再合并的方法。(不赘述)
二叉堆排序
一般利用堆,实际实现是利用数组的形式,进行维护top k的一种排序方法。
非比较排序
上面的方法,都是基于数与数的比较,来完成排序。而非比较排序,不需要比较数与数之间的关系。
你只要知道你前面有多少人,你就可以确定你的位置,进而排序。
计数排序
计数排序,是记录每个位置上应该出现的数字,比如【1, 3, 4, 5】,我们就可以建立一个数组【1, 0, 1, 1, 1】,表示有1个1,0个2, 1个3, 1个4, 1个5。
计数排序适用于相对集中的数据排序,如果【1, 100】, 那我们就要建立100位的计数数组。
基数排序
之前说了计数排序的弊端,那么基数排序是用来解决这个问题的。基数排序,从个位,十位,百位入手,对于个位进行计数排序,因为个位可能出现的数字最多只有10个,也就是基数。【1, 100】就被分为百位、十位、个位,三个基数排序。
桶排序
桶排序,可以大致理解位数据进行分块,但不是像归并排序那样随机分,而是有个相对顺序的区间,就比如100-90, 90-80一个桶,然后在桶内完成排序。
这种情况适用于数据相对多,而且数据大小分布相对均匀的情况下。