一些排序算法

看算法第四版整理的关于排序算法的部分。


下面的排序思路都默认做升序排序

选择排序

思路

找到数组中最小的元素,将其和数组首元素交换位置。再找到剩下元素中最小的元素,将其和数组第二个元素交换位置,如此往复。

评估

选择排序需要大概 N 2 / 2 N^2 / 2 N2/2次比较,和 N N N次交换。

  • 优点
    数据移动最少,交换次数和数组的大小是线性关系
  • 缺点
    无论输入如何排序时间不会减少。

插入排序

思路

维护一个指针从数组首元素开始每次向右递增一个元素,永远保证指针左侧的数组是有序的。
即每向右移一位,就将该元素与其左侧元素比较,若小则交换位置,直到将其放到正确的位置上。

评估

假设数组长度为N,倒置1数量为T。
插入排序的比较为 T T T ~ T + N − 1 T+N-1 T+N1,交换次数为 T T T

  • 优点
    倒置越少,排序越快。对部分有序的数组十分高效,也很适合小规模数组。

发扬这两个优点就得到了希尔排序。

希尔排序

思路

对于插入排序,每次只移动一个位置十分低效,希尔排序就是解决这个问题的。
其想法是使数组成为h有序数组,即其任意间隔为h的元素都是有序的。

过程是这样的:首先确定一个递增序列,即h从1到大的一个序列。

递增序列会影响排序时间,但找到一个好的递增序列比较麻烦。

然后从最大的h开始,对每个h,用插入排序将其所有子数组独立地排序。
然后将h改为其序列中更小一个的值。重复。
直至最后h为1时就是一次插入排序。

这个过程中h从大到小,子数组由短变长,逐渐变得有序。

评估

希尔排序高效的原因是它权衡了子数组的规模和有序性。
从最开始的规模十分小但不太有序,到最后虽然规模大但十分有序。这两点不同时很差时使用插入排序不会很差。

  • 优点
    对于较大数组插入排序不适合,但希尔排序就可以。

且除非数组十分大,否则希尔排序比之后的复杂排序效率低不了太多。

归并排序

思路

利用分治思想,把两个有序的数组合并起来。

评估

比较次数为 1 2 N l g N \frac{1}{2}NlgN 21NlgN ~ N l g N NlgN NlgN

  • 优点
    最差的时间复杂度也是 O ( N l o g N ) O(NlogN) O(NlogN)
  • 缺点
    空间复杂度为 O ( N ) O(N) O(N)

归并排序分为两种:

  • 自顶向下归并
    所谓自顶向下,就是从整个数组不断划分到最小的数组,然后递归地归并回去。
  • 自底向上归并
    所谓自底向上,就是从最小的数组开始两两归并,然后四四归并,直到整体有序。
    这种归并代码量较小,且比较适合链表存储的数据(因为是按序访问,而非随机访问)。

快速排序

思路

也使用分治的思想,选择一个元素作为切分点,保证其左侧所有元素都小于等于它,右侧元素都大于等于它。
然后分别对其最侧和右侧两个数组进行排序。

评估

最多需要 N 2 2 \frac{N^2}{2} 2N2次比较,但概率极小,平均只需 N l n N NlnN NlnN次比较。

  • 优点
    快,且是原地排序,不太需要额外空间。
  • 改进
    对于有大量重复元素的数组,不用二分而采用三向切分
    所谓三向切分是指,选择一个基数后将数组分为三段,额外维护两个“指针”lt和gt,使得
    a r r [ l o , l t − 1 ] < b a s e = a r r [ l t , g t ] < a r r [ g t + 1 , h i ] arr[lo, lt-1] < base = arr[lt, gt] < arr[gt+1, hi] arr[lo,lt1]<base=arr[lt,gt]<arr[gt+1,hi]
    这样可以使得相等的元素不会被递归处理,从而大幅度提高效率,使得时间复杂度减少到 O ( N ) O(N) O(N)

堆排序

二叉堆

二叉堆这个数据结构是一组能够用堆有序2的完全二叉树排序的元素。

当某个结点比起父结点更大时就需要进行上浮操作,反之则需要下沉

思路

首先将需要排序的数组构建为堆有序的。
然后利用堆的有序性进行排序(过程类似于选择排序)。

  • 构造
    最简单的构造方法是从首元素开始对所有元素进行上浮操作。
    但由于数组是一颗完全二叉树,即数组中后一半的元素都是叶子结点,因此可以从中间 k = N 2 k=\frac{N}{2} k=2N处开始,对前一半所有元素进行下沉操作。这样就节省了一半的时间。
  • 排序
    取出最大元素(即数组首元素),于最末尾元素交换,然后将新首部下沉以修复堆。
    将待排序数组大小减1并重复,直到排序成功。

评估

比较次数最多为 2 N l g N + 2 N 2NlgN+2N 2NlgN+2N,其中2N来自堆的构造。交换次数是比较数的一半。

要注意构造堆的时间复杂度是 O ( N ) O(N) O(N)

  • 优点
    唯一能够最优地利用时间和空间的排序
    最慢的时间复杂度也是 O ( N l o g N ) O(NlogN) O(NlogN)(快速排序最差为 O ( N 2 ) O(N^2) O(N2)),且不需要额外空间(有相同最差时间复杂度的归并排序的空间复杂度为 O ( N ) O(N) O(N)
  • 缺点
    不能很好利用缓存。比较和交换的元素通常不相邻。

总结

算法平均时间复杂度最差时间复杂度空间复杂度是否稳定
选择排序 O ( N 2 ) O(N^2) O(N2) O ( N 2 ) O(N^2) O(N2) O ( 1 ) O(1) O(1)×
插入排序 O ( N 2 ) O(N^2) O(N2) O ( N 2 ) O(N^2) O(N2) O ( 1 ) O(1) O(1)
希尔排序 O ( N l o g N ) O(NlogN) O(NlogN) O ( N 2 ) O(N^2) O(N2) O ( 1 ) O(1) O(1)×
归并排序 O ( N l o g N ) O(NlogN) O(NlogN) O ( N l o g N ) O(NlogN) O(NlogN) O ( N ) O(N) O(N)
快速排序 O ( N l o g N ) O(NlogN) O(NlogN) O ( N 2 ) O(N^2) O(N2) O ( l o g N ) O(logN) O(logN)×
堆排序 O ( N l o g N ) O(NlogN) O(NlogN) O ( N l o g N ) O(NlogN) O(NlogN) O ( 1 ) O(1) O(1)×

快速排序的空间复杂度来自于递归调用。


  1. 一对顺序颠倒的元素 ↩︎

  2. 一颗二叉树的每个结点都不小于其子结点时称为堆有序 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值