本文所涉及的算法代码实现都放在github仓库,需要可自取:https://github.com/chenruoyu0319/data-structure-for-java/tree/main/%E6%8F%92%E5%85%A5%26%E5%B8%8C%E5%B0%94%26%E5%BD%92%E5%B9%B6%EF%BC%88%E6%8E%92%E5%BA%8F%EF%BC%89
快速排序、冒泡排序,希尔排序,二分排序(二路归并)O(nlogn),桶排序,堆排序,基数排序,插入O (n^2),选择排序。
插入&希尔&归并排序:递进
选择&冒泡&快速:递进
我们通常从哪几个方面来分析一个排序算法?
1.时间效率:决定了算法运行多久
2.空间复杂度:需要开辟多大的空间
3.比较次数&交换次数:排序肯定会牵涉到两个操作,比较和交换
4.稳定性:指相同的两个数排完序后,相对位置不变。
比如1 9 3 5 3
第一种:1 3 3 5 9
第二种:1 3 3 5 9
第一种就是稳定的。
稳定排序有什么意义?应用在哪里呢?
比如电商里面订单排序:首先会按金额从小到大排,金额相同的按下单时间。从订单中心过来的时候已经按照时间排好序了。
序号 时间 金额 1 8:01 65 2 20:05 30 3 21:10 30 4 22:01 45
如果我选择不稳定的排序算法 那我还要比较两次的,如果我选择稳定的排序算法,那我就只要比较一个字段。
假设有个这样的问题:打扑克。分成两部分:一部分是你手里的牌(已经排好序),一部分是要拿的牌(无序)。把一个无序的数列一个个插入到有序数列中。
一个有序的数组,我们往里面添加一个新的数据后,如何继续保持数据有序呢?我们只要遍历数组,找到数据应该插入的位置将其插入即可。
画图演示:
以上这种往一个有序的集合里面插入元素,插入后序列仍然有序这就是插入排序算法思路。理解起来不难吧。那么插入排序具体是怎么实现呢?
具体步骤如下:
1.将数组分成已排序段和未排序段。初始化时已排序端只有一个元素
2.到未排序段取元素插入到已排序段,并保证插入后仍然有序
3.重复执行上述操作,直到未排序段元素全部加完。
按照这个思路,用一个数组就能实现一个插入排序,其时间复杂度是O(n^2)
希尔排序其实是插入排序的一个改进版。他是怎么改进呢?
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
其实就是分成很多小组使序列尽可能的变成有序段,因为我们通过对插入排序分析可知,插入排序对已经排好序的序列速度是很快的。
来看一个具体的过程:7 8 9 0 4 3 1 2 5 10
按照一个增量分段:add=n/2 n=10 =>5,2,1
7 8 9 0 4 3 1 2 5 10
我们取的这个增量分别就是5 2 1
7 8 9 0 4 3 1 2 5 10:分出来的数组元素都是一样的
完成一次排序:
3 1 2 0 4 7 8 9 5
3 2 4 8 5:取增量为2的分为一组了
最后一次我们就取增量为1的分组:
就是一个完整的序列
时间复杂度也是O(n^2),只是相较于插入排序有了部分提升
归并排序是一种非常高效的排序算法,其核心思想就用到了递归和分治的思想,那么具体是怎么样的呢?举例实现:
假设我们对以下序列排序:
9 5 6 8 0 3 7 1
归并排序分析:
主要分析时间复杂度:nlogn
其他的就和插入排序是一样的
1.时间复杂度
插入排序:O(n^2)
希尔排序:O(n^2)
归并排序:O(nlogn)
2.空间复杂度
3.稳定性:
插入排序是稳定的吗?稳定
希尔排序呢?不稳定的
归并排序呢?同插入排序