八大排序算法的比较
以下将从排序思想、算法稳定性、时间复杂度和空间复杂度四个方面对八大排序算法(冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、基数排序)进行详细比较。
1. 冒泡排序
- 思想:重复走访要排序的数列,一次比较两个相邻元素,如果顺序错误就把它们交换过来,每一轮会将最大(或最小)的元素 “浮” 到数列末尾,直到整个数列有序。
- 稳定性:稳定。因为只有相邻元素比较且顺序错误时才交换,相等元素不会交换,相对顺序不变。
- 时间复杂度:
- 最好情况(数列已有序):O(n),只需遍历一次,无元素交换。
- 最坏和平均情况:O(
),需要进行大量比较和交换。
- 空间复杂度:O(1),只需要常数级额外空间用于交换元素。
2. 选择排序
- 思想:每一轮从待排序元素中选出最小(或最大)的元素,与待排序序列的第一个元素交换位置,不断缩小待排序序列,直到整个数列有序。
- 稳定性:不稳定。例如
[5, 8, 5, 2]
,选最小 2 与第一个 5 交换,两个 5 相对顺序改变。 - 时间复杂度:无论数列初始状态如何,都需进行n(n - 1)/2次比较,时间复杂度始终为O(
) 。
- 空间复杂度:O(1),只需要常数级额外空间用于交换元素。
3. 插入排序
- 思想:将未排序数据插入到已排序序列的合适位置。初始时,第一个元素视为已排序,从第二个元素开始,依次将其插入到前面已排序序列的正确位置。
- 稳定性:稳定。插入过程中,相等元素会插入到相等元素后面,相对顺序不变。
- 时间复杂度:
- 最好情况(数列已有序):O(n),只需遍历一次,每个元素只需比较一次。
- 最坏和平均情况:O(
),每个元素可能需要移动多个位置。
- 空间复杂度:O(1),只需要常数级额外空间用于元素移动。
4. 希尔排序
- 思想:是插入排序的改进版,先将整个待排序的记录序列分割成若干个子序列,分别对这些子序列进行直接插入排序,随着增量逐渐减小,子序列长度增加,整个序列变得越来越接近有序,最后进行一次直接插入排序。
- 稳定性:不稳定。由于不同子序列的插入操作,可能会改变相等元素的相对顺序。
- 时间复杂度:与增量序列的选择有关,平均情况约为 O(
) 到 O(
) 之间。
- 空间复杂度:O(1),只需要常数级额外空间用于元素移动。
5. 归并排序
- 思想:采用分治法,将数列分成两个子数列,分别对两个子数列进行排序,然后将排好序的子数列合并成一个最终的有序数列。不断递归地分割和合并,直到整个数列有序。
- 稳定性:稳定。在合并过程中,相等元素会按顺序放入合并后的序列,相对顺序不变。
- 时间复杂度:无论最好、最坏还是平均情况,时间复杂度都是O(nlogn) ,因为每次分割和合并的时间复杂度都是 O(logn)和 O(n)。
- 空间复杂度:O(n),需要额外的数组空间来合并子序列。
6. 快速排序
- 思想:同样采用分治法,选择一个基准元素,将数列分为两部分,使得左边部分的元素都小于等于基准元素,右边部分的元素都大于等于基准元素,然后分别对左右两部分递归进行排序。
- 稳定性:不稳定。在分区过程中,可能会改变相等元素的相对顺序。
- 时间复杂度:
- 最好和平均情况:O(nlogn),每次分区能均匀分割数列。
- 最坏情况(数列已有序):O(
),每次分区只能将数列分成一个元素和其余元素两部分。
- 空间复杂度:
- 平均情况:O(logn),递归调用栈的深度为 logn。
- 最坏情况:O(n),递归调用栈的深度达到 n。
7. 堆排序
- 思想:利用堆这种数据结构,先将数列构建成一个最大堆(或最小堆),然后将堆顶元素(最大值或最小值)与最后一个元素交换,再调整堆,重复此过程,直到整个数列有序。
- 稳定性:不稳定。在堆调整和交换过程中,可能会改变相等元素的相对顺序。
- 时间复杂度:无论最好、最坏还是平均情况,时间复杂度都是O(nlogn) ,因为每次调整堆的时间复杂度为 O(logn),需要进行 n次调整。
- 空间复杂度:O(1),只需要常数级额外空间用于元素交换。
8. 基数排序
- 思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。从最低位开始,依次对每一位进行排序,直到最高位,最终得到有序数列。
- 稳定性:稳定。在按位排序过程中,相等元素会按顺序放入排序结果,相对顺序不变。
- 时间复杂度:O(d(n+k)),其中 d是最大数字的位数,n是数列元素个数,k是每一位的取值范围(如十进制为 10)。
- 空间复杂度:O(n+k),需要额外的空间来存储临时数据。
排序算法 | 排序思想 | 稳定性 | 时间复杂度(平均) | 空间复杂度 | 适用场景 |
---|
冒泡排序 | 重复走访要排序的数列,交换相邻元素,直到整个数列有序。 | 稳定 | O(n²) | O(1) | 小规模数据,简单场景 |
选择排序 | 每一轮选出最小(或最大)元素,与第一个元素交换,直到整个数列有序。 | 不稳定 | O(n²) | O(1) | 小规模数据 |
插入排序 | 将未排序数据插入到已排序序列的合适位置,每次插入一个元素直到整个数列有序。 | 稳定 | O(n²) | O(1) | 部分有序数据,数据量小 |
希尔排序 | 将数列分割成多个子序列,对每个子序列进行插入排序,逐渐减少间隔,最后进行一次插入排序。 | 不稳定 | O(n^(3/2)) | O(1) | 大规模数据,部分有序数据 |
归并排序 | 采用分治法将数列分割成子数列,排序并合并子数列,直到整个数列有序。 | 稳定 | O(n log n) | O(n) | 大规模数据,稳定性要求高 |
快速排序 | 采用分治法,通过基准元素分割数列,递归对左右部分进行排序。 | 不稳定 | O(n log n) | O(log n) | 大规模数据,适用于平均情况较好的数据 |
堆排序 | 利用堆结构调整元素,每次取堆顶元素(最大或最小),再调整堆,直到整个数列有序。 | 不稳定 | O(n log n) | O(1) | 大规模数据,内存受限 |
基数排序 | 按位数切割数列,逐位进行排序,从最低位到最高位,最终得到有序数列。 | 稳定 | O(d(n+k)) | O(n+k) | 整数数据,数值范围有限的数据 |