高级排序算法与性能对比------归并、非比较排序及总结
1. 归并排序
基本思想 :
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,采用分治法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
核心步骤:分解 -> 合并。
代码实现:
c
void _MergeSort(int* a, int left, int right, int* tmp) {
if (left >= right) return;
int mid = (right + left) / 2;
// [left, mid] [mid+1, right]
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = begin1;
// 合并两个有序数组
while (begin1 <= end1 && begin2 <= end2) {
if (a[begin1] < a[begin2]) tmp[index++] = a[begin1++];
else tmp[index++] = a[begin2++];
}
while (begin1 <= end1) tmp[index++] = a[begin1++];
while (begin2 <= end2) tmp[index++] = a[begin2++];
for (int i = left; i <= right; i++) a[i] = tmp[i];
}
void MergeSort(int* a, int n) {
int* tmp = new int[n];
_MergeSort(a, 0, n - 1, tmp);
delete[] tmp;
}
特性总结:
- 时间复杂度:(O(N \log N)),非常稳定。
- 空间复杂度:(O(N))。
- 稳定性:稳定。
2. 非比较排序:计数排序
基本思想 :
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
- 统计相同元素出现次数。
- 根据统计的结果将序列回收到原来的序列中。
优化技巧 :如果数据范围较大但集中(如100-109),可以按范围开空间,使用 a[i] - min 作为索引。
代码实现:
c
void CountSort(int* a, int n) {
int min = a[0], max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) max = a[i];
if (a[i] < min) min = a[i];
}
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
memset(count, 0, sizeof(int) * range);
// 统计次数
for (int i = 0; i < n; i++) count[a[i] - min]++;
// 排序
int j = 0;
for (int i = 0; i < range; i++) {
while (count[i]--) a[j++] = i + min;
}
free(count);
}
特性总结:
- 时间复杂度:(O(N + range))。
- 空间复杂度:(O(range))。
- 稳定性:稳定。
- 适用场景:数据范围集中时效率极高,但适用范围有限。
3. 排序算法复杂度及稳定性分析
3.1 稳定性定义
假定在待排序的记录序列中,存在多个具有相同关键字的记录。若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定 的;否则称为不稳定。
3.2 综合对比表
| 排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
|---|---|---|---|---|---|
| 冒泡排序 | (O(n^2)) | (O(n)) | (O(n^2)) | (O(1)) | 稳定 |
| 直接选择排序 | (O(n^2)) | (O(n^2)) | (O(n^2)) | (O(1)) | 不稳定 |
| 直接插入排序 | (O(n^2)) | (O(n)) | (O(n^2)) | (O(1)) | 稳定 |
| 希尔排序 | (O(n \log n) \sim O(n^2)) | (O(n^{1.3})) | (O(n^2)) | (O(1)) | 不稳定 |
| 堆排序 | (O(n \log n)) | (O(n \log n)) | (O(n \log 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^2)) | (O(\log n) \sim O(n)) | 不稳定 |
3.3 稳定性验证案例
- 直接选择排序 :
5 8 5 2 9(第一个5会被交换到后面,导致不稳定)。 - 希尔排序 :
5 8 2 5 9(相同元素可能被分到不同组,导致相对位置改变)。 - 堆排序 :
2 2 2 2(建堆和调整过程中可能改变相同元素的相对顺序)。 - 快速排序 :
5 3 3 4 3 8 9 10 11(基准值交换可能导致相同元素顺序改变)。
4. 总结
在实际开发中:
- 若追求平均性能 ,首选快速排序。
- 若要求稳定性 ,首选归并排序。
- 若数据基本有序 ,插入排序效率极高。
- 若数据范围集中 ,计数排序是最优解。
- 若考虑空间受限 ,堆排序是很好的选择。