前言:生活中的排序处处可见,今天我们来学习排序中的思想和细节。
文章目录
一、直接插入排序
基本思想:我们平时玩扑克牌时,摸牌阶段的排序就用到了插入排序的思想
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)
三、选择排序
![](https://i-blog.csdnimg.cn/blog_migrate/fb0136688c6bca747d468fbe7863c3b6.gif)
基本思想:
每次从数组中选出一个最大的和一个最小的,存放在数组的最右边和最左边,直到全部有序
单趟实现:
每次可以排好两个数,数组区间逐渐递减到left<=right便排好序了。
整体代码:
选择排序总结:
①直接选择排序很好理解,效率不高,很少使用,无论数据最好或者最坏的情况时间复杂度都不变,因此实用性不高
②时间复杂度:O(N^2)
③空间复杂度:O(1)
四、堆排序
基本思想:
1、堆的逻辑结构是完全二叉树,而物理结构却是存放在数组中连续的。
2、向上和向下调整法建堆
3、排升序建大堆,降序建小堆
1、向上调整法
基本思想:
1、顾名思义从孩子依次往上调整
2、保证前面的元素构成堆
3、模拟数据插入的过程向上调整,若插入的元素比它的双亲大,则需要交换他们的位置,再迭代向上调整,直到孩子比双亲小。
4、从头到尾遍历完整个数组便实现了堆的构建
![](https://i-blog.csdnimg.cn/blog_migrate/a4d18d42d356fd43295096a6993ee0d5.png)
2、向下调整法
![](https://i-blog.csdnimg.cn/blog_migrate/de9f83f3978b802c144d0364881ad571.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/925cf22e2a2ba661171f352ffc39d565.png)
3、向下调整排序
基本思想:
1、前提是已经构建了大堆
2、模拟堆的删除过程,将堆顶的数字与最后一个数字交换进行升序排列
3、每次排序完一个数,向下调整还原堆
![](https://i-blog.csdnimg.cn/blog_migrate/616765cda0ca69da84900ee0f4161ee6.png)
堆排序总结:
①先建堆,升序大堆,降序小堆,这样可能尽可能保证数据之间兄弟的相对情况
②时间复杂度:O(N^logN)
③空间复杂度:O(1)
五、冒泡排序
![](https://i-blog.csdnimg.cn/blog_migrate/cf35a370bd8aa7f391fec5e402b175d2.gif)
基本思想:
一趟过程中,前后两个数依次比较,将较大的数字往后推,依次循环进行
![](https://i-blog.csdnimg.cn/blog_migrate/b20b16a6b7f68b414b3254dcea7da255.png)
冒泡排序总结:
①前面大于后面则交换,每次排好一个最大值
②时间复杂度: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]
![](https://i-blog.csdnimg.cn/blog_migrate/d1c1171c8a43662d7e7fbb82a18c932d.png)
2、挖坑法
基本思想
1、先将最左边第一个数据存放在临时变量key中,形成一个坑位
2、从反方向右边先出发找到小于key值的下标,然后将该下标对应值丢到坑中去,这个下标形成一个新坑位
3、左边后出发找到大于key的值的下标,将该值丢入坑中去,此时该下标又成为新的坑位
![](https://i-blog.csdnimg.cn/blog_migrate/f6da9322f6e1185134e614754035d6fa.png)
3、前后指针法
基本思想:
1、cur找到比key小的值,++prev,cur和prev位置的值交换,++cur
2、cur找到比key大的值,++cur
3、prev要么紧跟着cur (prev下一个就是cur)2、prev跟cur中间间隔着比key大的一段值区间
4、把比key大的值往右翻,比key小的值,翻到左边
![](https://i-blog.csdnimg.cn/blog_migrate/6db5aad378cc2e5297c5f49a67fd4a7d.png)
4、递归的优化
1、 三数取中
可以看出快排越接近二分效率越高,因此我们要尽量选择中间数组的中间值当作key。我们在这里采用三数取中的方法
2、 递归到最小子区间
随着递归深度的增加,递归次数以每层2倍的速度增加,这对效率有着很大的影响,我们只需要去掉递归的最后几层,数据量就少了60%~70%
3、当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,当递归区间长度小于10时我们就用插入排序解决
5、非递归版本
非递归的基本思想:
1、将需要排序的区间从右到左依次压入栈中,栈的性质是后进先出
2、找到keyi后,把[keyi+1]和[begin,keyi-1]先后压入栈中
3、当区间长度小于二时不在入栈,一个数据不需要排序
![](https://i-blog.csdnimg.cn/blog_migrate/d31bea16530bd91cd1e63346c73936bd.png)
快排总结
快速排序的总结:
①快排应对一般情况排序速度快,效率高
②快排局限,面对特殊场景排一些有序或者接近有序的序列效率极低,比如3,2,2,2,2,2,2,2,3,3,2,3这种序列,会变成O(N^2)的时间复杂度
③时间复杂度O(N*logN)
④空间复杂度O(logN)
七、归并排序
递归化实现
基本思想
1、(拆分)将数据从中间二分为左序列和右序列,再将左序列细分为左序列和右序列,如此重复该步骤,直到细分到区间不存在或者只有一个数字为止
2、当区间只有一个数字的时候开始归并,直到递归回溯到第一层
3、(合并)将上一次归并得到的数据合并成有序区间,依次进行
![](https://i-blog.csdnimg.cn/blog_migrate/f3c6218e7d811bb1d7305e16784c11d2.png)
非递归实现
基本思想:
1、通过操作数组的下标实现归并,从一 一归并到二 二归并到归并完整个数组
2、数据个数为奇数或者不是 2 n 2^n 2n就会出现归并时边界越界情况
3、边界问题分析,一 一解决
![](https://i-blog.csdnimg.cn/blog_migrate/e60ba0907dbbb4fc409fcf906c19cf69.png)
归并排序总结; >①优点,当数据量非常庞大时,归并排序可以将数据分块,变成小部分一 一解决 ②时间复杂度:O(N*logN) ③空间复杂度:O(N) 需要额外开辟一个数组移动
八、计数排序
基本思想:
1、利用用相对映射关系,先找到最大最小元素之差,开辟一个存放某元素出现次数的数组,下标即为最大和最小值的相对位置
2、统计相同元素出现的个数
3、根据统计的结果,将数据拷贝回原数组
![](https://i-blog.csdnimg.cn/blog_migrate/d2dfd1a622d6d2489e2a60a0fac177b9.png)
计数排序总结:
1、计数排序适合范围集中,且范围不大的整形数组排序。不适合范围分散或者非整形的排序,如:字符串、浮点数等
2、时间复杂度O(N)
3、空间复杂度O(N)
九、排序的稳定性总结
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[j]=r[i],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的
![](https://i-blog.csdnimg.cn/blog_migrate/f0b31ab299a2cab14cbb01ec048f430f.png)
稳定的排序有:直接插入排序、冒泡排序、归并排序
不稳定的排序有:希尔排序、选择排序、堆排序、快速排序、计数排序
这一篇博客到此就结素了,希望排序知识能对你有所帮,下一篇见!