![74779891909ec371f954d745756bdcf2.png](https://i-blog.csdnimg.cn/blog_migrate/6db8bf853ff0b649009a90f60ec774e9.png)
排序算法在程序设计和公司面试中不可或缺。
排序算法的稳定性:在一个序列里,如果有若干值一样的记录,在经过排序算法排序后,这些相同值的相对位置没有发生改变,那么该算法稳定,否则不稳定。
排序算法可分为:插入排序,交换排序,选择排序,归并排序和分配排序。
1插入排序
直接插入排序
插入排序:插入排序的思想就是在待排序列中,第一个位置假定有序,在后面的待排序列中,依次比较,插入到有序序列中,以此类推,直到无序序列数为0。
![9f520470cd04c8c16cedb0e03b74a0ce.png](https://i-blog.csdnimg.cn/blog_migrate/8982df01b480ccb84a2e007d8ea777d1.png)
直接插入排序代码如下:
![f64be6dd0ed06d223f6e9c93897d5b8a.png](https://i-blog.csdnimg.cn/blog_migrate/a688156a41ef3a05e1d8760e5e9ce615.png)
平均时间复杂度为O(n2)。
希尔排序
希尔排序对直接插入排序进行了改进。希尔排序是将待排序列分成若干组,对每个组进行直接插入排序,然后对整个序列直接插入排序。步骤如下:假设待排序列长度为n。
![75ead9fc4f6abb87ece68fe534d3c25a.png](https://i-blog.csdnimg.cn/blog_migrate/55d140f4445b9e3a8f923b861200b983.jpeg)
希尔排序并不稳定,性能优于插入排序,但不如快速排序,在问题规模中等时表现良好。希尔排序代码其实就是在直接插入排序上稍作改进,代码如下:
![cf98e9ca37361af4bbbaa013ae0d438d.png](https://i-blog.csdnimg.cn/blog_migrate/b16e7e60c5bf0d6cddf29373acfd2c6f.png)
2交换排序
交换排序:交换排序我们在学习中用的比较多,在交换排序里我们介绍冒泡排序和快速排序算法。
冒泡排序
冒泡排序:对一个待排序列,我们按相邻元素两两比较,若第一个比第二个大,就交换,一直到最后一个元素,此时,最后一个元素为最大值,然后重复上述过程,直到序列有序。代码如下:
![0c856b13985748c9710eca7fd9f4788a.png](https://i-blog.csdnimg.cn/blog_migrate/1a9e1c6c53841a02416f9876c7fb1793.png)
快速排序
快速排序:快速排序是一个经典的算法,在面试或程序设计中经常被用到。它的思想是对一组待排序列,首先选一个阈值(通常为数组第一个元素),然后对此进行一次快速排序,即最终结果是阈值的左边都比阈值小,阈值的右边都比阈值大。然后对阈值的左右分别递归快速排序。一次快速排序我们用Partition函数实现。
![17663113d385d02efe21d653725e4f26.png](https://i-blog.csdnimg.cn/blog_migrate/a2f0e5280ef77cce55059a71a1e2dc14.png)
![e47fbf5a6ab8a335be17a3653d7b3737.png](https://i-blog.csdnimg.cn/blog_migrate/e3b8b2f03127e368b8627d2538ca234e.jpeg)
代码实现如下:
![8776c7074f78b2b53a98fdd20881ffc2.png](https://i-blog.csdnimg.cn/blog_migrate/2c9fa8dd4f3924261ee614591360ce0e.jpeg)
3选择排序
简单选择排序
选择排序:选择排序实际上是将待排序列分为无序区和有序区,在无序区经过比较,找到最小值,放到有序区,直到完全有序。简单选择排序代码如下:
![f7749d07246f8a36c1359917566f60b0.png](https://i-blog.csdnimg.cn/blog_migrate/cda2e987037314029312077a6b051fa6.png)
对于选择排序,我们要重复的比较关键码的大小,因此在效率上并不高。为了提高效率,减少比较次数,我们使用堆排序。
堆排序
堆排序也是一个难点,堆分为大根堆和小根堆,是一种完全二叉树,大根堆每个结点大于等于左右子结点,小根每个结点小于等于左右子结点。堆排序的思想是将待排序列调整成小根堆,此时堆顶为最大值,将堆顶与堆尾交换,剩下的数据再调整成堆,以此类推。
堆排序需要解决三个问题,(1)如何将无序序列调整成大根堆;(2)如何处理堆顶元素;(3)如何将剩余数据重新调整成大根堆。
(1)建堆的顺序是从最后一个分支结点开始进行堆调整。若二叉树最后一个结点为n,那么n/2就是最后一个分支结点。(这里用到了完全二叉树的性质),那么对n调整,要找出其左右子节点的最大值来与n比较,若结点n的值大,不变,否则调换。
(2)将堆顶元素与堆尾元素交换。
(3)剩余元素除了堆顶,其余地方都有序,因此再调用一次shift即可。
代码如下:(这里一定要注意边界条件,小编调Bug调了半天)
![f597cdd7d43d08d3cd5526f0d359c497.png](https://i-blog.csdnimg.cn/blog_migrate/5f290f0d5bfe5c754b425e85f73e4bd8.jpeg)
4归并排序
归并排序:所谓归并,即将两个或两个以上有序序列合并成一个有序序列的过程。归并排序就是对归并的递归。这里要注意的是,归并算法需要额外的数组,因此空间复杂度为O(n),有了辅助空间,归并算法的时间性能就会大大改善。
二路归并排序
将两个有序序列合并成一个有序序列,称为二路合并。
![42d776401f23e05f4e3fcd460711b0fb.png](https://i-blog.csdnimg.cn/blog_migrate/8df7dc15ae6253a2977c3780d51e854f.jpeg)
归并排序要解决的主要问题是实现一次归并,要使用到额外的数组。首先解决如何合并两个有序序列,比较两个序列的第一个数,谁小取谁,一直循环,直到一个序列为空,让剩余的数接在后面。
![7d602b994520569d1ccee77dd6d3c64b.png](https://i-blog.csdnimg.cn/blog_migrate/9031c4097094133c4c0102578c66996d.png)
![f5b3927d567019ea52f61f1b83d436f5.png](https://i-blog.csdnimg.cn/blog_migrate/e0b5da68630ee3e87c82d80d54601574.jpeg)
5分配排序
上面介绍的排序都是根据键值进行比较来排序,分配排序不进行关键码的比较,而是将序列中的数据分配,然后再收集起来。
桶排序
即桶是按编号摆放,我们将数据按编号放到对应的桶里,那么这些数据就自然有序了,前提是桶要按顺序摆放。有关桶式排序的代码,会在后面的链表章节给出。
基数排序
基数排序是桶排序的改进,当排序为小数时,为了减少桶的数量,可以采用多键值排序。
![5e70e627a1e18b78eb2773d43c0fd8db.png](https://i-blog.csdnimg.cn/blog_migrate/7f89d219e37b0378cc9d418ba9c4bf3b.jpeg)
基数排序适用于关键码较少的情况,效率比较高。
6各种排序算法比较
不稳定排序:希尔排序,快速排序,堆排序。
![d3acf4b0bf82be78a8cb7ec6526e0aa7.png](https://i-blog.csdnimg.cn/blog_migrate/c13c6b9c639b3aa20ffe85c4878e46dc.jpeg)
本文主要参考数据结构相关资料和《剑指offer》,本文所有代码均为本人编写并经过测试,如有错误或问题,欢迎留言或私信指正。
![633aebd801f28f268ed9d9e4933d2814.png](https://i-blog.csdnimg.cn/blog_migrate/5c7bb10b3695e154895bce8910a11fcc.png)