万字详解八大排序

前言:生活中的排序处处可见,今天我们来学习排序中的思想和细节。

一、直接插入排序

基本思想:我们平时玩扑克牌时,摸牌阶段的排序就用到了插入排序的思想
1、当插入第n个元素时,前面的n-1个数已经有序
2、用这第n个数与前面的n-1个数比较,找到要插入的位置,将其插入
3、插入的数小于原来位置上的数据时,依次后移

单趟实现:
  从下面可以看出无论是提前跳出循环,还是end<0跳出循环,插入位置都是end+1。
下面是排序过程:

  接下来我们模仿数组插入的过程,把第i个元素赋值给tmp并把它当作要插入的元素,从头到尾遍历一遍数组,便完成了插入排序。

插入排序总结:
①元素越接近有序,直接插入排序的效率越高

②时间复杂度:O(N^2)
最坏的情况下,每次插入一个数字,前面的数字都要挪动一下,一共需要挪动1+2+3+……+n=n(n+1)/2

③空间复杂度:O(1)
在原来的数组上操作,空间复杂度O(1)

二、希尔排序

基本思想:
1、先选定个小于size的数字作为gap,区间[0,gap)我为要排的组数。在该区间中找一个起始位置,在把所有距离为gap的数分为一组进行预排序(直接插入排序)
2、gap越大,数据跳得越远,预排越快,越不接近于有序。gap越小,数据跳得越近,预排越慢,越接近有序
3、再选一个小于gap的数,重复第一步操作。当gap==1时,相当于整个数组就是一组,这时候就是希尔排序就是插入排序。

单趟实现:

  有了单趟排序,只需要让gap逐渐变为1,那么就希尔排序就完成了。这里我们采用gap/=2的方式,这样gap必会回到1
下面是排序过程:

整体代码:

希尔排序总结
①希尔排序是对直接插入排序的优化,通过预排序使数据相对有序

②时间复杂度:O(N^1.3)

③空间复杂度:O(1)
和直接插入排序一样在原数组中进行移动,空间复杂度为O(1)

三、选择排序

基本思想:
每次从数组中选出一个最大的和一个最小的,存放在数组的最右边和最左边,直到全部有序

单趟实现:

  每次可以排好两个数,数组区间逐渐递减到left<=right便排好序了。
整体代码:

选择排序总结:

①直接选择排序很好理解,效率不高,很少使用,无论数据最好或者最坏的情况时间复杂度都不变,因此实用性不高

②时间复杂度:O(N^2)

③空间复杂度:O(1)

四、堆排序

基本思想:
1、堆的逻辑结构是完全二叉树,而物理结构却是存放在数组中连续的。
2、向上和向下调整法建堆
3、排升序建大堆,降序建小堆

1、向上调整法


基本思想:
1、顾名思义从孩子依次往上调整
2、保证前面的元素构成堆
3、模拟数据插入的过程向上调整,若插入的元素比它的双亲大,则需要交换他们的位置,再迭代向上调整,直到孩子比双亲小。
4、从头到尾遍历完整个数组便实现了堆的构建

2、向下调整法

>基本思想: 1、顾名思义,先有双亲再找孩子依次往下比较调整 2、保证后面的元素构成堆 3、所以我们从倒数第一个非叶结点开始向下调整,找到比双亲大的左右孩子,让双亲和左右孩子大的那个孩子交换 4、循环从该叶子结点遍历到根结点就结束

3、向下调整排序

基本思想:
1、前提是已经构建了大堆
2、模拟堆的删除过程,将堆顶的数字与最后一个数字交换进行升序排列
3、每次排序完一个数,向下调整还原堆

堆排序总结:

①先建堆,升序大堆,降序小堆,这样可能尽可能保证数据之间兄弟的相对情况

②时间复杂度:O(N^logN)

③空间复杂度:O(1)

五、冒泡排序

基本思想:
一趟过程中,前后两个数依次比较,将较大的数字往后推,依次循环进行

冒泡排序总结:
①前面大于后面则交换,每次排好一个最大值

②时间复杂度:O(N^2)

③空间复杂度:O(1)

六、快速排序

  快速排序有多种不同版本和优化,下面我们依次学习

1、hoare版本

基本思想;
1、快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
2、选一个值作为key,然后在右边找小,左边找大,找到一组后交换
3、key值在哪边则从其反面开始走,比如选a[left]作key,则先从右边找小,再从左边找大,这样可以保证key值得相遇位置一定比a[left]小
4、最后相遇时交换a[left]和a[key]

2、挖坑法

基本思想
1、先将最左边第一个数据存放在临时变量key中,形成一个坑位
2、从反方向右边先出发找到小于key值的下标,然后将该下标对应值丢到坑中去,这个下标形成一个新坑位
3、左边后出发找到大于key的值的下标,将该值丢入坑中去,此时该下标又成为新的坑位

3、前后指针法

基本思想:
1、cur找到比key小的值,++prev,cur和prev位置的值交换,++cur
2、cur找到比key大的值,++cur
3、prev要么紧跟着cur (prev下一个就是cur)2、prev跟cur中间间隔着比key大的一段值区间
4、把比key大的值往右翻,比key小的值,翻到左边

4、递归的优化

1、 三数取中
可以看出快排越接近二分效率越高,因此我们要尽量选择中间数组的中间值当作key。我们在这里采用三数取中的方法
2、 递归到最小子区间
随着递归深度的增加,递归次数以每层2倍的速度增加,这对效率有着很大的影响,我们只需要去掉递归的最后几层,数据量就少了60%~70%
3、当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,当递归区间长度小于10时我们就用插入排序解决

5、非递归版本

非递归的基本思想:
1、将需要排序的区间从右到左依次压入栈中,栈的性质是后进先出
2、找到keyi后,把[keyi+1]和[begin,keyi-1]先后压入栈中
3、当区间长度小于二时不在入栈,一个数据不需要排序

快排总结

快速排序的总结:
①快排应对一般情况排序速度快,效率高
②快排局限,面对特殊场景排一些有序或者接近有序的序列效率极低,比如3,2,2,2,2,2,2,2,3,3,2,3这种序列,会变成O(N^2)的时间复杂度
③时间复杂度O(N*logN)
④空间复杂度O(logN)

七、归并排序

递归化实现

基本思想
1、(拆分)将数据从中间二分为左序列和右序列,再将左序列细分为左序列和右序列,如此重复该步骤,直到细分到区间不存在或者只有一个数字为止
2、当区间只有一个数字的时候开始归并,直到递归回溯到第一层
3、(合并)将上一次归并得到的数据合并成有序区间,依次进行

非递归实现

基本思想:
1、通过操作数组的下标实现归并,从一 一归并到二 二归并到归并完整个数组
2、数据个数为奇数或者不是 2 n 2^n 2n就会出现归并时边界越界情况
3、边界问题分析,一 一解决

  看图可知,当end1或者begin2越界时不需要归并,原区间已是有序,而当end2越界时,要把边界修复成size-1
归并排序总结; >①优点,当数据量非常庞大时,归并排序可以将数据分块,变成小部分一 一解决 ②时间复杂度:O(N*logN) ③空间复杂度:O(N) 需要额外开辟一个数组移动

八、计数排序

基本思想:
1、利用用相对映射关系,先找到最大最小元素之差,开辟一个存放某元素出现次数的数组,下标即为最大和最小值的相对位置
2、统计相同元素出现的个数
3、根据统计的结果,将数据拷贝回原数组

计数排序总结:

1、计数排序适合范围集中,且范围不大的整形数组排序。不适合范围分散或者非整形的排序,如:字符串、浮点数等
2、时间复杂度O(N)
3、空间复杂度O(N)

九、排序的稳定性总结

  稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[j]=r[i],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

总结

稳定的排序有:直接插入排序、冒泡排序、归并排序
不稳定的排序有:希尔排序、选择排序、堆排序、快速排序、计数排序

  这一篇博客到此就结素了,希望排序知识能对你有所帮,下一篇见!

猜你喜欢

转载自blog.csdn.net/Front123456/article/details/130068208