1. 选择排序(Selection Sort)
算法思想与理论推导
-
基本思想:
每次从待排序数组中选择最小(或最大)的元素,将它与当前序列的起始位置交换,逐步将整个数组排序。 -
推导过程:
设数组长度为 n,第一次遍历需要扫描 n 个元素找到最小值,第二次扫描 (n-1) 个元素……直到最后一个元素。
总的比较次数约为:
算法步骤
-
对于下标 i 从 0 到 n-2:
-
在区间 [i, n-1] 内找到最小元素的下标 min_index。
-
若 min_index ≠ i,则交换元素 arr[i] 与 arr[min_index]。
-
时间复杂度分析
-
最坏情况: O(n²)
-
平均情况: O(n²)
-
空间复杂度: O(1)(原地排序)
-
稳定性: 不稳定(因为交换可能改变相同元素的相对顺序)
2. 插入排序(Insertion Sort)
算法思想与理论推导
-
基本思想:
将数组分为已排序和未排序两部分,每次取未排序部分的第一个元素,在已排序部分中找到合适的位置插入,使已排序部分始终保持有序。 -
推导过程:
对于每个元素,最坏情况下需要和前面所有元素比较并移动位置。第 i 个元素可能需要 i 次比较和移动,总的操作次数约为:当数组已经接近有序时,比较次数大大减少,时间复杂度可以达到 O(n)。
算法步骤
-
从数组第二个元素开始(下标 1):
-
记当前元素为 key,从已排序部分从后向前比较,若遇到比 key 大的元素则后移。
-
插入 key 到合适的位置。
-
时间复杂度分析
-
最坏情况: O(n²)(例如数组逆序排列)
-
平均情况: O(n²)
-
最好情况: O(n)(例如数组已经有序)
-
空间复杂度: O(1)
-
稳定性: 稳定
3. 冒泡排序(Bubble Sort)
算法思想与理论推导
-
基本思想:
通过多次遍历数组,每次比较相邻元素,如果顺序错误则交换,将较大(或较小)的元素逐步“冒泡”到数组的一端。 -
推导过程:
每次遍历最多做 n-1 次比较,整个排序需要进行 n-1 次遍历,所以总的比较次数约为:
算法步骤
-
重复遍历整个数组,
-
对于每一对相邻元素,如果前者大于后者则交换。
-
每遍历完一次后,最大的元素“浮”到数组末尾。
-
-
如果一整趟遍历没有发生交换,则排序结束。
时间复杂度分析
-
最坏情况: O(n²)
-
平均情况: O(n²)
-
最好情况: 若增加标志位检测无交换情况,则最好情况为 O(n)(已经有序)
-
空间复杂度: O(1)
-
稳定性: 稳定
4. 归并排序(Merge Sort)
算法思想与理论推导
-
基本思想:
利用分治法,将数组递归地分解为两个子序列,分别排序后再将两个有序子序列合并成一个整体有序序列。 -
推导过程:
设 T(n) 为排序 n 个元素的时间复杂度,则:根据主定理,解得 T(n) = O(n log n)。
算法步骤
-
分解阶段:
-
将数组分为两半。
-
递归地对两半分别进行归并排序。
-
-
合并阶段:
-
合并两个有序子数组。
-
时间复杂度分析
-
最坏情况: O(n log n)
-
平均情况: O(n log n)
-
最好情况: O(n log n)
-
空间复杂度: O(n)(需要额外的辅助空间用于合并)
-
稳定性: 稳定
5. 自然合并排序(Natural Merge Sort)
算法思想与理论推导
-
基本思想:
利用数据中已存在的自然有序(“天然有序”的子序列或“段”),先将序列分割成若干个自然有序的段,再逐步将这些段两两归并。 -
推导过程:
如果输入数据已经部分有序,则自然有序的段较长,合并次数减少,最佳情况时间复杂度可接近 O(n)。
最坏情况仍然为归并排序的情况,即当数据完全无序时,各自然段长度为 1,总共需要 O(log n) 次归并,每次归并时间 O(n),故为 O(n log n)。
算法步骤
-
识别自然段:
-
扫描数组,识别连续递增(或递减)的一段作为一个“自然段”。
-
-
归并阶段:
-
将相邻的自然段两两合并,形成新的较长的自然段。
-
重复归并过程直到整个数组有序。
-
时间复杂度分析
-
最坏情况: O(n log n)
-
平均情况: O(n log n)
-
最好情况: 如果数组整体有序,识别出一个自然段,时间复杂度 O(n)
-
空间复杂度: O(n)(归并时需要辅助数组)
-
稳定性: 稳定
6. 快速排序(Quick Sort)
算法思想与理论推导
-
基本思想:
利用分治策略选择一个“基准”(这里取第一个元素),将数组分成两部分:左边所有元素均小于基准,右边所有元素均大于基准,然后对这两部分递归进行排序。 -
推导过程:
设 T(n) 为排序 n 个元素的时间复杂度,则:其中 k 表示划分后左侧子数组的大小。
-
平均情况: 当每次划分较为均匀(大致各半),有 T(n) = 2T(n/2) + O(n),解得 T(n) = O(n log n)
-
最坏情况: 当划分极不均匀(如数组已排序,且始终选择最小或最大元素作为基准),有 T(n) = T(n-1) + O(n),解得 T(n) = O(n²)。
-
算法步骤
-
选择基准:
-
选择第一个元素作为基准。
-
-
分区操作:
-
从数组两端向中间扫描,将比基准小的元素移到左边,比基准大的元素移到右边。
-
-
递归排序:
-
对左右两个子数组分别进行快速排序。
-
时间复杂度分析
-
最坏情况: O(n²)(例如每次划分极不均匀)
-
平均情况: O(n log n)
-
最好情况: O(n log n)
-
空间复杂度: O(log n)(递归调用栈空间,平均情况下)
-
稳定性: 不稳定
算法对比
算法 | 时间复杂度(最坏/平均/最好) | 空间复杂度 | 稳定性 | 特点与适用场景 |
---|---|---|---|---|
选择排序 | O(n²) / O(n²) / O(n²) | O(1) | 不稳定 | 实现简单,但比较次数固定,不适合大规模数据排序 |
插入排序 | O(n²) / O(n²) / O(n) | O(1) | 稳定 | 对部分有序数据效果好,适用于小规模数据或在线排序 |
冒泡排序 | O(n²) / O(n²) / O(n)(优化版) | O(1) | 稳定 | 实现简单,但效率低,更多用于教学演示 |
归并排序 | O(n log n) / O(n log n) / O(n log n) | O(n) | 稳定 | 时间复杂度稳定,适用于大规模数据,但需要额外空间 |
自然合并排序 | O(n log n) / O(n log n) / O(n) | O(n) | 稳定 | 能利用已有有序序列提升效率,适合部分有序数据 |
快速排序 | O(n²) / O(n log n) / O(n log n) | O(log n)(平均) | 不稳定 | 平均性能优异,原地排序,适合大规模数据,但最坏情况需注意 |
总结
-
简单排序(选择、插入、冒泡):
这些算法实现简单,但时间复杂度均为 O(n²)(在最坏或平均情况下),适合数据量较小的情况。插入排序在数据部分有序时表现较好,而冒泡排序常用作教学示例。 -
分治排序(归并、快速、自然合并):
归并排序和快速排序均有 O(n log n) 的平均时间复杂度,其中归并排序稳定但需要额外空间,而快速排序在原地排序方面有优势。自然合并排序则利用了数据中已有的有序性,在实际应用中对于部分有序数据可获得较好性能。