4000字总结迄今为止大多数排序的精华

Insertion sort(插入排序)

基于’比较交换‘的排序规则
在这里插入图片描述
时间复杂度 O(n^2)
空间复杂度 O(1)
算法稳定,排完序后相同关键字的相对顺序没有改变

Bubble sort(冒泡排序)

基于’比较交换‘的排序规则
每次找出还未排序的部分中的最大值放在末尾
详情传送门
在这里插入图片描述
时间复杂度 O(n^2)
空间复杂度 O(1)
算法稳定,排完序后相同关键字的相对顺序没有改变

Heap sort(堆排)

基于’比较交换‘的排序规则
树必须是完全二叉树,可以用数组来实现

算法基于左右两棵子树都是堆,所以算法应该是从最小子树开始,最后一个元素的一半即为该元素的父结点,也是最小子树,且左右两颗子树皆为完整堆(只有一个元素)

优势:即使是最坏的情况下,时间复杂度仍为O(nlogn)
空间复杂度为O(1)
这里说明一下空间复杂度为啥是O(1)
假设要求升序
则可以构建一个最大堆,出堆时的操作为返回根结点的值并将数组的最后一个元素移到根结点,此时根结点的左右子树都为完整堆,故可以构成新的堆,再将元素个数-1。
关键来了,此时堆如果是数组的话,那么最后一个空间存储的就是这个数组的最大值,且因为元素个数减掉了1,所以后面的循环不会改变这个数的位置,可以类比下冒泡,都是从后面开始排。

这里是不是有个疑惑:既然堆排在最坏情况下时间复杂度都是O(nlogn) 且空间复杂度为O(1),那么为啥应用最广泛的是快排而不是堆排?

缓存利用率低
很简单,堆排的时候,数据都是跳跃式的移动,而快排则是尽快的移动到最终的位置,然后做小范围的跳动。所以快排更容易缓存命中。
平均时间上快排还是要快一点
平均时间上,堆排序的时间常数比快排要大一些,因此通常会慢一些,但是堆排序最差时间也是O(nlogn)的,这点比快排好。

Quick sort(快速排序)

简而言之就是选择一个基本元,然后将数据分为3部分
比基本元小的数 < 基本元 < 比基本元大的数

其中比较不固定的地方就是基本元的选取,目前应用最广的有两种方式,随机数,中位数,考虑到随机数的开销,这里还是推荐中位数
在这里插入图片描述
优点:平均时间最快,时间复杂度为O(nlogn),空间复杂度为O(1)
有个老哥写的很好传送门
快排的前身是归并,而正是因为归并存在不可忽视的缺点,才产生了快排。归并的最大问题是需要额外的存储空间,并且由于合并过程不确定,致使每个元素在序列中的最终位置上不可预知的。针对这一点,快速排序提出了新的思路:把更多的时间用在“分”上,而把较少的时间用在“治”上。从而解决了额外存储空间的问题,并提升了算法效率。

快排之所以被称为“快”排,是因为它在平均时间上说最快的,主要原因是硬件方面的,每趟快排需要指定一个“支点”(也就是作为分界点的值),一趟中涉及的所有比较都是与这个“支点”来进行比较的,那么我们可以把这个“支点”放在寄存器里,如此这般,效率自然大大提高。除此之外,快排的高效率与分治思想也是分不开的。
这里提一点,因为快排用到了递归,所以对于硬件方面存在入栈出栈开销,在数据规模很小的情况下,速度可能还没有冒泡快。

缺点:极端情况下时间复杂度会上升为O(n^2)
快排的时间复杂度主要依赖主元的选取,如果主元每次刚好选在中间,那么时间复杂度为nlogn,如果主元每次都选在了最大值或最小值的位置,那么就是n方了。

AVL sort(平衡二叉树排序)

时间复杂度O(nlogn)
空间复杂度O(取决于你需要多少个指针)
这里必须得是真的二叉树了,无法用数组代替,所以有一个天然的劣势就是额外的数据开销,至少包含一个左子树的指针和一个右子树的指针。

但是它的好处可太多了
首先它解决了数组的插入困难,在数组中每次要插入一个数你得将这个数后面的数据依次向后面移一个位置,然后再进行插入
其次它解决了链表的查询困难在链表中如果要查询一个数得遍历整个链表而无法像数组一样下标查找
最重要的是它提供了额外的数据,这一点是堆排无法提供的如果我们查找到一个数,我们同样可以知道这个数的larger和smaller,且是最接近的那个,访问也很简单,分别为它的左子树和右子树的key。

说了这么多但还是没有说咋个排序,很简单,用我们刚学树的时候就知道的知识–中序遍历,至于为什么你试一下就知道来

Merge sort(归并排序)

很多人刚开始学归并的时候会觉得归并只能用数组实现,其实并不是,链表一样可以
有一种方法叫快慢指针法传送门

好处:
归并是典型的外排算法(排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序),有个老哥写的不错传送门

应对于数据量非常大的数据,这些数据放在硬盘上,无法一次性的放到内存上。所以,我们通常对这些数据进行切分,然后将切分出来的小文件读进内存进行快排,最后得到的就是一堆排好序的小文件,然后在从其中两个文件中一个一个读数据进行归并,边读边存
在这里插入图片描述
缺点:需要额外的O(n)辅助空间,这点没有快排好,但是快排无法处理这种大文件问题
稳定性:稳定

Counting sort(计数排序)

目前以上的算法,最快时间复杂度也是O(nlogn),有没有一种更快的算法
有,计数排序的时间复杂度为O(n+k)n为数据规模,k为数据范围

需要提一下的是
对于’比较交换‘类的算法,它的好处就是几乎不需要额外的空间,但同样的时间复杂度被卡在了O(nlogn)
但是计数排序是基于’一眼扫过‘整个数组,记录下每个数出现的次数,再进行排序的算法

计数排序虽然快,但是缺点一大堆
1.需要提前知道数据规模和数据范围
2.假如有两个相同的数,你无法区别先后关系,因为数组里只会记录这个数出现的次数不会记录先后关系 ,需要需要记录先后关系的话,归并是个不错的选择
3.数据规模不能太大,内存无法一口气存下这么大规模的数据
4.不适用于带有负数的排序,需要额外的逻辑开销,得不偿失

Radix sort(基数排序)

在计数排序基础上建立的排序,时间复杂度仍然是线性
思想就是按位排序,先排最低位,最后排最高位传送门
在这里插入图片描述
缺点:
1.还是没有解决存在负数的问题
其余问题都得到了解决是计数排序的优化版本

Shell sort(希尔排序)

目前最优的增量序列可以使时间复杂度降为O(n^4/3)
具体实现直接去看mooc的浙大数据结构就好
在这里插入图片描述
缺点:如果增量序列选择不好,时间复杂度甚至比插入排序还慢,如
在以下的数组中,前面在1间隔之前,没有做任何交换
在8间隔中下标为0,8的值为1,5为有序的,没有进行交换
下标为1,9的值为9,13为有序,也没有进行交换
同理在1间隔之前数组还是原来的数组,这时进行排序其实就是插入排序
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钧11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值