常用的排序方法

常用的排序方法

  • 排序算法的稳定性是指:在需要进行排序操作的数据中,如果存在值相等的元素,在排序前后,相等元素之间的排列顺序不发生改变

  • 字母 n 表示排序的数组或链表的元素个数

  • 排序的对象是:数组或链表

比较

最好时间复杂度最坏时间复杂度平均时间复杂度空间复杂度排序方式稳定性排序对象备注
冒泡排序(Bubble Sort)O(n)O(n2)O(n2)O(1)In-Place稳定数组
直接插入排序(Insertion Sort)O(n)O(n2)O(n2)O(1)In-Place稳定数组、链表
归并排序(Merge Sort)O(n log n)O(n log n)O(n log n)O(n)Out-Place稳定数组、链表
计数排序(Count Sort)O(n + k)O(n2)O(n + k)O(k)Out-Place稳定数组、链表待排序的数据必须是有确定范围的整数
桶排序(Bucket Sort)O(n + k)O(n2)O(n + k)O(n + k)Out-Place稳定数组、链表待排序的数据必须是有确定范围的整数、计数排序的升级版
基数排序(Radix Sort)O(n * k)O(n * k)O(n * k)O(n + k)Out-Place稳定数组、链表待排序的数据必须是有确定范围的整数
选择排序(Selection Sort)O(n2)O(n2)O(n2)O(1)In-Place不稳定数组、链表
堆排序(Heap Sort)O(n log n)O(n log n)O(n log n)O(1)In-Place不稳定数组
快速排序(Quick Sort)O(n log n)O(n2)O(n log n)O(n log n)In-Place不稳定数组
希尔排序(Shell’s Sort)O(n log2 n)O(n log2 n)O(n log n)O(1)In-Place不稳定数组直接插入排序的升级版、适合初始数据偏向有序的数据

稳定的排序算法

冒泡排序(Bubble Sort)

排序方式 和 对象
  • In-Place(在正确的位置),数据对象是:数组
时间复杂度
  • 平均:O(n2)

  • 最好:O(n),此时,第一次排序后,元素全部归位,第二次比较时确认一下即可跳出循环。

  • 最坏:O(n2),比较到最后才把全部元素归位

空间复杂度
  • O(1),只需要添加一个元素用来交换数组元素,与数组大小无关。
原理
  • 比较相邻的元素,如果前者大于(小于)后者,就交换两者

  • 从第一对(0 和 1)到最后一对(n-2 和 n-1),重复比较;交换完到最后一对时,最后一个元素应该是最大(最小)的数

  • 剔除上一次循环归位的元素后,重复以上的步骤,继续循环。

  • 直到剩余数据只有一位;此时排序完成。或者,在当前循环中检测到所有元素都已归位,此时结束当前循环后,排序结束

  • 注意:定义一个布尔变量 hasChange,用来标记每轮是否进行了交换,不交换时,数组所有元素都已归位。可以结束排序了。

  • 在每轮遍历开始时,将 hasChange 设置为 false;若当轮没有发生交换(hasChange == false),说明此时数组已经按照升序(降序)排列,可以直接结束排序了

插入排序(直接插入排序,Insertion Sort)

排序方式
  • In-Place(在正确的位置),数据对象是:数组链表
时间复杂度
  • 平均:O(n2)

  • 最好:O(n)

  • 最坏:O(n2)

空间复杂度
  • O(1),只需要添加一个元素用来交换数组(链表)元素,与数组(链表)大小无关。
原理
  • 插入算法的核心思想是:取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入

  • 我们将数组(链表)中的数据分为两个区间:已排序区间未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素

  • 插入排序每次会从未排序区间中找一个元素(这个元素一般是去未排序区第一位元素),在已排序区间中找到合适的插入位置将其插入,保证已排序区间的数据一直有序。

  • 重复这个过程,直到未排序区间中元素为空,算法结束。

  • 注意:无论怎样都无法提前结束的排序算法的

归并排序(Merge Sort)

排序方式
  • Out-Place(在不正确的位置),数据对象是:数组链表
时间复杂度
  • 平均:O(n log n)

  • 最好:O(n log n)

  • 最坏:O(n log n)

空间复杂度
  • O(n) 或 O(n) + O(log n)
原理
  • 归并排序的核心思想是分治:把一个复杂问题拆分成若干个子问题来求解

  • 把数组(链表)从中间划分为两个子数组(子链表)。

  • 一直递归地把子数组(子链表)划分成更小的数组(子链表)直到子数组(子链表)是完全有序的数组(链表)。(一般数组或链表里面只有一个元素的时候是可以保证是完全有序的)

  • 归并数组(链表)时:按照大小顺序合并两个子数组(子链表)。接着依次按照递归的顺序返回,不断合并排好序的数组(链表),直到把整个数组(链表)排好序。

计数排序(Count Sort)

排序方式
  • Out-Place(在不正确的位置),数据对象是:数组链表

  • 要求:待排序的数据必须是有确定范围的整数

时间复杂度
  • 平均:O(n + k)

  • 最好:O(n + k)

  • 最坏:O(n2)

  • k:是待排序的数组(链表)中的最大的元素减去最小的元素加上一,即:输入的元素的范围

空间复杂度
  • O(k),需要额外的空间存储元素出现的次数。
原理
  • 找出待排序的数组(链表)中的最大的元素(max)最小的元素(min)

  • 创建数组 C[max-min+1],用来存储待排序的数组(链表)每个元素出现的次数,存放方式:值减 min 等于 i 的元素出现的次数存放在 C[i]。

  • 也就是说:i = 元素值 - min

  • 数组 C 的长度取决于:待排序数组(链表)中数据的范围,即 max - min + 1

  • 累加所有元素出现的次数(从数组 C 的第一个元素累加到最后一个元素,默认项是没有值的),这个值就是目标数组(链表)的长度。

  • 遍历数组 C 输出元素,其中元素值为:i + min,元素出现次数为:C[i]。(输出 C[i] 次 i+min)

桶排序(箱排序,Bucket Sort)

排序方式
  • Out-Place(在不正确的位置),数据对象是:数组链表

  • 桶排序计数排序升级版

时间复杂度
  • 平均:O(n + k)

  • 最好:O(n + k)

  • 最坏:O(n2)

空间复杂度
  • O(n + k)
原理
其一:箱子一一对应元素
  • 设置一个定量的数组当作空桶(空箱),这样的空桶(空箱)有 k 个

  • 设定每个桶的对应一个元素(范围内的),那么桶的数量大于等于:排序的数组(链表)中的最大的元素(max)- 最小的元素(min)+ 1

  • 遍历输入数据,并且把数据一个一个放到对应的桶里去,相同的元素放到同一个桶中,不同的元素放在不同的桶中

  • 那么很可能会有桶是空的,对每个非空的桶进行桶间排序(每个桶要按照桶内元素来排序),空桶没有后续操作了。

  • 遍历排序好的非空桶的序列,按照排序顺序将非空的桶里的数据拼接起来桶内的多个元素拼接到一起

其二:箱子对应一定范围内的元素
  • 设置一个定量的数组当作空桶(空箱),这样的空桶(空箱)有 k 个

  • 设定每个桶的对应一定区间内的元素(范围内的),区间一般是同样长度的,那么桶的数量大于等于:排序的数组(链表)中的(最大的元素(max)- 最小的元素(min)+ 1)/区间长度

  • 遍历输入数据,并且把数据一个一个放到对应的桶里去,相同区间的元素放到同一个桶中,不同区间的元素放在不同的桶中

  • 那么很可能会有桶是空的,对每个非空的桶进行桶间排序(每个桶要按照桶内元素区间来排序),空桶没有后续操作了。

  • 桶内元素分别要进行排序,方便进行后续遍历。

  • 遍历排序好的非空桶的序列,按照排序顺序将非空的桶里的数据拼接起来桶内的区间元素按照排序好的顺序拼接到一起

基数排序(Radix Sort)

排序方式
  • Out-Place(在不正确的位置),数据对象是:数组链表

  • 字母 k 表示排序的数组元素的最大位数(就是最大值的位数)。

  • 基数排序属于分配式排序(Distribution Sort),又称桶子法(Bucket Sort 或 Bin Sort)

  • 基数排序桶排序扩展

  • 要求:待排序的数据必须是有确定范围的整数

时间复杂度
  • 平均:O(n * k)

  • 最好:O(n * k)

  • 最坏:O(n * k)

空间复杂度
  • O(n + k)
原理
  • 基数排序的原理是:将整数按位数切割成不同的数字,然后按每个位数分别比较

  • 确定排序的数组(链表)元素的最大位数(Max 位)(就是最大值的位数),表示需要执行循环的轮数

  • 创建十个桶,表示:0~9,桶是用队列来实现的。(所有的数字元素都是由 0~9 的十个数字组成)

  • 根据循环执行轮数依次判断每个元素的个位、十位、百位至 Max 位

  • 循环时,根据元素的当前比较位(个位、十位、百位至 Max 位)来选择该元素要放入那个桶;该元素没有该比较位时存入 0 号数据桶,该元素该比较位数值等于 1 时存入 1 号数据桶,以此类推。

  • 每轮循环结束后,按照 0~9 号顺序,将数据桶中的数据存回原数组(链表)中,而同一个桶内的元素连续存放,出桶顺序就是进桶顺序(队列)

  • 比较完最大位数(Max 位),元素归位到原数组(链表)中,算法结束

疑问
  • 如何保证位数相同、数值不等、若干位相同的元素按照数值大小排列呢?(如:11 和 12、21 和 31 等)

    • 11 和 12 是高位相同,在个位排序时,11 去到 1 数字桶,12 去到 2 数字桶,归位到原数组时,12 一定在 11 后面。

    • 11 和 12 在十位排序时,11 和 12 都去到 1 数字桶,因为桶是队列结构,归位到原数组时,12 也一定在 11 后面。

    • 11 和 12 在百位排序及之后 m 位排序时,11 和 12 都去到 0 数字桶,因为桶是队列结构,归位到原数组时,顺序不变

    • 21 和 31 是低位相同,在个位排序时,顺序不变;在十位排序时,21 去到 2 数字桶,31 去到 3 数字桶,归位到原数组时,31 一定在 21 后面。

    • 21 和 31 在百位排序及之后 m 位排序时,21 和 31 都去到 0 数字桶,因为桶是队列结构,归位到原数组时,顺序不变。

不稳定的排序算法

选择排序(Selection Sort)

排序方式
  • In-Place(在正确的位置),数据对象是:数组链表。以链表为对象时,是稳定的算法。
时间复杂度
  • 平均:O(n2)

  • 最好:O(n2)

  • 最坏:O(n2)

空间复杂度
  • O(1),只需要添加一个元素用来交换数组(链表)元素,与数组(链表)大小无关。
原理
  • 我们将数组(链表)中的数据分为两个区间:已排序区间未排序区间。初始已排序区间没有元素。

  • 选择排序每次会从未排序区间中找到最小(最大)的元素,将其放到已排序区间的末尾,保证已排序区间的数据一直有序。

  • 重复这个过程,直到未排序区间中元素为空,算法结束。

  • 注意:无论怎样都无法提前结束的排序算法的

堆排序(Heap Sort)

  • :是一种常用的树形结构,是一种特殊的完全二叉树当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树。
排序方式
  • In-Place(在正确的位置),数据对象是:数组
时间复杂度
  • 平均:O(n log n)

  • 最好:O(n log n)

  • 最坏:O(n log n)

空间复杂度
  • O(1),只需要添加一个元素用来交换数组元素,与数组大小无关。
原理
  • 堆排序是指:利用这种数据结构所设计的一种排序算法。

  • 每个结点的值都大于等于其左右子结点的值,称为大顶堆(大根堆)

  • 每个结点的值都小于等于其左右子结点的值,称为小顶堆(小根堆)

  • 初始待排序数组构建成大顶堆此堆为初始的无序区初始的有序区为空

  • 堆顶元素R[0]最后一个元素R[n-1]交换,此时无序区减一得到新的无序区(R[0]、R[1]、...、R[n-2]);有序区加一得到新的有序区(R[n-1])。且满足R[0,1,2…n-2] <= R[n-1]。

  • 由于交换后新的堆顶R[0]可能违反堆的性质,因此需要对当前无序区(R[0]、R[1]、...、R[n-2])进行调整,重新排序为新堆

  • 不断重复 交换元素重排堆 的操作,直到无序区元素为一个或有序区元素为 n-1 个时,完成排序。

快速排序(Quick Sort)

排序方式
  • In-Place(在正确的位置),数据对象是:数组
时间复杂度
  • 平均:O(n log n)

  • 最好:O(n log n)

  • 最坏:O(n2),当基准点每次都选择到最坏的情况。(基准点每次都是数组的最边缘的元素)

空间复杂度
  • O(log n)
原理
  • 快速排序的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序

  • 从数组中挑出一个元素,设定为 基准(pivot),是一个分界值。

  • 重新排序数组,所有元素比基准值小的摆放在基准前面所有元素比基准值大的摆在基准的后面,相同的数可以到任一边。

  • 基准值为界限,将重新排序好的数组分为前后两个子数组(基准可以放到任一个子数组中),两个子数组又可以执行 找基准、重新排序 的操作。

  • 重复上述过程,当各部分排序完成后,整个数组就排序完成了

优化快速排序
  • 最理想的基准点是:被基准点分开的两个子数组中,数据的数量差不多

  • 优化快速排序就是指:尽量每次寻找到的基准点都接近最理想的基准点

三数取中法
  • 我们从数组的首、尾、中间,分别取出一个数,然后对比大小,取这 3 个数的中间值作为分区点(基准点)

  • 这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好

  • 但是,如果要排序的数组比较大,那三数取中可能就不够了,可能要五数取中或者十数取中

随机法
  • 随机法就是每次从要排序的区间中,随机选择一个元素作为分区点(基准点)

  • 这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次选择的分区点(基准点)都很差的情况

  • 所以平均情况下,这样选的分区点是比较好的

  • 时间复杂度退化为最糟糕的 O(n²) 的情况,出现的可能性不大。

希尔排序(Shell’s Sort)

排序方式
  • In-Place(在正确的位置),数据对象是:数组

  • 插入排序的升级版。

  • 希尔排序适合初始数据基本无序的数据。

  • 插入排序适合初始数据偏向有序的数据。

时间复杂度
  • 平均:O(n log n)

  • 最好:O(n log2 n)

  • 最坏:O(n log2 n)

空间复杂度
  • O(1)
原理
  • 待排序的数组按照下标的一定增量(增量:gap)进行分组。(就是同组的元素的下标差是增量:gap

  • 每组元素进行直接插入排序元素间只能在组内互相交换位置,不能改变每个组所包含的数组下标集。

  • 增量 gap 每次循环会变为原来的一半(取整),即:gap = gap / 2

  • 随着增量 gap 的减少,分组越少,组内元素越多,当增量 gap 减至 1 时,整个序列恰好被分为一组,对组内元素排序后,算法终止。

  • 推荐 gap 初始值取数组长度的一半(取整)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十⑧

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

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

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

打赏作者

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

抵扣说明:

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

余额充值