🛰️排序算法
2022-5-18
| 2023-6-16
0  |  阅读时长 0 分钟
type
status
password
date
slug
summary
category
URL
tags
icon

排序
稳定性
排序方式
最坏
平均
最好
空间复杂度
冒泡排序
稳定
内部
选择排序
稳定
内部
插入排序
稳定
内部
希尔排序
不稳定
内部
堆排序
不稳定
内部
快速排序
不稳定
内部
平均 最优 最坏
归并排序
稳定
外部
计数排序
稳定
外部
桶排序
稳定
外部
基数排序
稳定
外部
  • 稳定:在原序列中,r[i]=r[j],而且r[i]r[j] 之前;排序之后,r[i]仍在r[j]之前,则称这种排序算法是稳定的
  • 不稳定:在原序列中,r[i]=r[j],而且r[i]r[j] 之前;排序之后,r[i]可能出现在r[j]后面,则称这种排序算法是不稳定的
桶排序的平均时间复杂度为线性的,其中。 如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到。 当然桶排序的空间复杂度为,如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。 此外,桶排序是稳定的。
💡
数学上的时间复杂度不代表实际运行时的情况
堆排比较的几乎都不是相邻元素,对cache极不友好,这才是很少被采用的原因。

冒泡排序

它重复地走访过要排序的数列,依次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
notion image

改进的冒泡排序

上面代码中里面一层的循环,如果某次扫描中没有执行交换,则说明此时数组已经全部有序列。进一步研究我们发现,如果R[i..n]已是有序区间,上次的扫描区间是R[0..i],记上次扫描时最后一次执行交换的位置为lastSwapPos,则lastSwapPos在0与i之间,不难发现R[lastSwapPos..n]区间也是有序的,否则这个区间也会发生交换;所以下次扫描区间就可以由R[0..i]缩减到[0..lastSwapPos]
  • 空间复杂度:假设数组的元素个数是 ,整个排序的过程中,直接在给定的数组里进行元素的两两交换。
  • 时间复杂度:
      1. 情景一:给定的数组按照顺序安排好只需要进行 次的比较,两两交换次数为 0,时间复杂度 ,这是最好的情况。
      1. 情景二:给定的数组按照逆序排列需要进行 次比较,时间复杂度是 ,这是最坏的情况。
      1. 情景三:给定的数组杂乱无章在这种情况下,平均时间复杂度是

选择排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
notion image

插入排序(链式存储方式)

通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
notion image
  • 空间复杂度:假设数组的元素个数是 ,整个排序的过程中,直接在给定的数组里进行元素的两两交换。
  • 时间复杂度:
      1. 情景一:给定的数组按照顺序安排好只需要进行 次的比较,两两交换次数为 0,时间复杂度 ,这是最好的情况。
      1. 情景二:给定的数组按照逆序排列需要进行 次比较,时间复杂度是 ,这是最坏的情况。
      1. 情景三:给定的数组杂乱无章在这种情况下,平均时间复杂度是

希尔排序

希尔排序是简单插入排序的改进版。它与插入排序的不同之处在于,是将整个有序序列分割成若干小的子序列分别进行插入排序。下图的数组长度为10,其增量序列为:5(10/2),2(5/2),1(2/2),第一趟排序将增量为5的子序列进行插入排序,第二趟排序将增量为2的子序列进行插入排序,第三趟将增量为1的子序列进行插入排序,最终完成排序。
notion image

时间复杂度

希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为 ;而Hibbard增量的希尔排序的最坏时间复杂度为 ,平均的时间复杂度为 ,Hibbard增量序列的取法为

堆排序

堆是一个近似完全二叉树的结构,并同时满足子结点总是小于(或者大于)它的父节点。
堆排序的建堆复杂度为 ,循环调堆的复杂度是。总运行时间 。对于堆排序的最好情况与最坏情况的运行时间,因为最坏与最好的输入都只是影响建堆的运行时间 或者 ,而在总体时间中占重要比例的是循环调堆的过程,即 = 。因此最好或者最坏情况下,堆排序的运行时间都是 。而且堆排序还是 原地算法,所以空间复杂度是
notion image
用数组存储大顶堆
notion image
notion image
notion image

python API实现

归并排序

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
  • 空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间:;所以空间复杂度为:
notion image

快速排序

在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边部分,大于等于此数的放在右边部分,这个操作确保了这个数是处于正确位置的,再对左边部分数组和右边部分数组递归调用快速排序,重复这个过程。直到整个序列有序。
notion image

空间复杂度

快排并没有开辟空间,但是使用了递归,递归会开辟栈帧。每次递归所需要的空间大小都是一样的而且就算是第N次递归,每次递归所需的栈空间也是一样的,所以每次递归中需要的空间是一个常量,并不会随着的变化而变化,每次递归的空间复杂度就是。所以递归算法的空间复杂度 = 每次递归的空间复杂度 * 递归深度 = 每次递归的空间复杂度 * 递归深度
  • 最好情况:整个数组被分成两段长度相等的子数组时,递归深度是。递推式如下
    • 最坏情况:待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,复杂度是。递推式如下:

      时间复杂度

      • 最好情况:整个数组被分成两段长度相等的子数组时,复杂度是。递推式如下
        • 最坏情况:待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,复杂度是。递推式如下:

          代码

          C++

          python

          计数排序

          将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。 给 个输入元素,每个元素都是 范围内的整数,其中 也是整数,计数排序算法的空间复杂度是 ,空间复杂度是
          notion image

          桶排序

          桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序。
          • 桶排序的时间复杂度是 ,空间复杂度是
          如果要排序的数据有n个,我们把它们分在k个桶中,这部分算法的时间复杂度是 。这样每个桶里的数据就是 。每个桶内排序的时间复杂度就为 个桶就是 ;在每个桶排序好之后,将每个桶中的元素合并到一起的时间复杂度是 。综上,算法复杂度是。当桶的个数接近数据个数时,就是一个较小的常数,所以时间复杂度接近
          notion image

          基数排序

          基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
          设待排序的数组 ,数组中最大的数是 位数,基数为 (如基数为10,即10进制,最大有10种可能,即最多需要10个桶来映射数组元素)。处理一位数,需要将数组元素映射到 个桶中,映射完成后还需要收集,相当于遍历数组一遍,最多元素数为 ,则时间复杂度为 。所以,总的时间复杂度为
          notion image
           
          相关文章 :
        • 排序
        • Python dict和set的底层原理线段树与树状数组
          Loading...
          目录