冒泡排序:
执行流程:
① 从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置(执行完一轮后,最末尾那个元素就是最大的元素)
② 忽略 ① 中曾经找到的最大元素,重复执行步骤 ①,直到全部元素有序
优化:
如果序列已经完全有序,可以提前终止冒泡排序
优化:
如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数
复杂度:
最坏时间复杂度:O(n2)
最好时间复杂度:O(n)
平均时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:稳定
选择排序:
执行流程:
① 从序列中找出最大的那个元素,然后与最末尾的元素交换位置(执行完一轮后,最末尾的那个元素就是最大的元素)
② 忽略 ① 中曾经找到的最大元素,重复执行步骤 ①
复杂度:
最坏时间复杂度:O(n2)
最好时间复杂度:O(n2)
平均时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:不稳定
比较:
选择排序的交换次数要远远少于冒泡排序,平均性能优于冒泡排序
堆排序:
执行流程:
① 对序列进行原地建堆
② 重复执行以下操作,直到堆的元素数量为 1
交换堆顶元素与尾元素
堆的元素数量减 1
对 0 位置进行 1 次下滤(siftDown)操作
复杂度:
最坏时间复杂度:O(nlogn)
最好时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
比较:
堆排序可以认为是对选择排序的一种优化
插入排序:
执行流程:
① 在执行过程中,插入排序会将序列分为2部分(头部是已经排好序的,尾部是待排序的)
② 从头开始扫描每一个元素(每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序)
逆序对:数组 <2,3,8,6,1> 的逆序对为:<2,1> <3,1> <8,1> <8,6> <6,1>,共5个逆序对
插入排序的时间复杂度与逆序对的数量成正比关系,逆序对的数量越多,插入排序的时间复杂度越高
优化:
① 先将待插入的元素备份
② 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置
③ 将待插入元素放到最终的合适位置 0
优化:
在元素 v 的插入过程中,可以先二分搜索出合适的插入位置,然后再将元素 v 插入
复杂度:
最坏时间复杂度:O(n2)
最好时间复杂度:O(n)
平均时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:不稳定
归并排序:
执行流程:
① 不断地将当前序列平均分割成2个子序列,直到不能再分割(序列中只剩1个元素)
② 不断地将2个子序列合并成一个有序序列,直到最终只剩下1个有序序列
复杂度:
最坏时间复杂度:O(nlogn)
最好时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定
快速排序:
执行流程:
① 从序列中选择一个轴点元素(pivot)假设每次选择 0 位置的元素为轴点元素
② 利用 pivot 将序列分割成 2 个子序列
将小于 pivot 的元素放在pivot前面(左侧)
将大于 pivot 的元素放在pivot后面(右侧)
等于pivot的元素放哪边都可以
③ 对子序列进行 ① ② 操作
直到不能再分割(子序列中只剩下1个元素)
复杂度:
最坏时间复杂度:O(n2)
最好时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
空间复杂度:O(logn)
稳定性:不稳定
希尔排序
执行流程:
希尔排序把序列看作是一个矩阵,分成 m 列,逐列进行排序
从某个整数逐渐减为1 p当 m 为1时,整个序列将完全有序
因此,希尔排序也被称为递减增量排序
矩阵的列数取决于步长序列(step sequence)
比如,如果步长序列为{1,5,19,41,109,...},就代表依次分成109列、41列、19列、5列、1列进行排序,不同的步长序列,执行效率也不同
复杂度:
最坏时间复杂度:O(n4/3)
最好时间复杂度: O(n)
平均时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
十大排序算法复杂度: