目录
[1. sort 算法](#1. sort 算法)
[1.1 算法原理](#1.1 算法原理)
[1.2 函数签名](#1.2 函数签名)
[1.3 特点](#1.3 特点)
[1.4 应用举例](#1.4 应用举例)
[2. partial_sort 算法](#2. partial_sort 算法)
[2.1 算法原理](#2.1 算法原理)
[2.2 函数签名](#2.2 函数签名)
[2.3 特点](#2.3 特点)
[2.4 应用举例](#2.4 应用举例)
[3. stable_sort 算法](#3. stable_sort 算法)
[3.1 算法原理](#3.1 算法原理)
[3.2 函数签名](#3.2 函数签名)
[3.3 特点](#3.3 特点)
[3.4 应用举例](#3.4 应用举例)
[4. 三种排序算法的性能比较](#4. 三种排序算法的性能比较)
[4.1 测试环境](#4.1 测试环境)
[4.2 测试代码](#4.2 测试代码)
[4.3 测试结果](#4.3 测试结果)
[4.4 性能分析](#4.4 性能分析)
[5. 适用场景总结](#5. 适用场景总结)
[6. 代码优化建议](#6. 代码优化建议)
[7. 结论](#7. 结论)
前言
本文详细解析了STL中的三种排序算法:sort(混合排序,不稳定,O(nlogn))、partial_sort(堆排序思想,获取前N个元素,O(nlogk))和stable_sort(归并排序,稳定但需额外空间)。通过性能测试对比了它们的效率差异,并提供了具体应用场景建议:sort适用于通用排序,partial_sort适合Top-K问题,stable_sort用于需要保持元素顺序的情况。
1. sort 算法
1.1 算法原理
sort 算法是STL中最常用的排序算法,它实现了一种混合排序算法,通常是** introsort**(内省排序),结合了快速排序、堆排序和插入排序的优点:
- 快速排序:用于处理大部分情况,平均时间复杂度为 O(n log n)
- 堆排序:当快速排序的递归深度超过阈值时使用,保证最坏情况时间复杂度为 O(n log n)
- 插入排序:当数据量较小时使用,对于小规模数据效率更高
1.2 函数签名
cpp
template<typename RandomIt>
void sort(RandomIt first, RandomIt last);
template<typename RandomIt, typename Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
1.3 特点
- 不稳定排序:相等元素的相对顺序可能会改变
- 原地排序:不需要额外的存储空间
- 时间复杂度:平均 O(n log n),最坏 O(n log n)
- 空间复杂度:O(log n),用于递归调用栈
1.4 应用举例
例1:基本用法
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {5, 2, 9, 1, 5, 6};
std::sort(v.begin(), v.end());
std::cout << "Sorted: ";
for (int num : v) {
std::cout << num << " "; // 输出: 1 2 5 5 6 9
}
std::cout << std::endl;
return 0;
}
例2:使用自定义比较函数
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {5, 2, 9, 1, 5, 6};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // 降序排序
});
std::cout << "Sorted in descending order: ";
for (int num : v) {
std::cout << num << " "; // 输出: 9 6 5 5 2 1
}
std::cout << std::endl;
return 0;
}
2. partial_sort 算法
2.1 算法原理
partial_sort 算法用于对序列的前 N 个元素进行排序,而不是对整个序列排序。它使用堆排序的思想:
- 首先构建一个大小为 N 的最大堆
- 然后遍历剩余元素,将每个元素与堆顶元素比较
- 如果当前元素小于堆顶元素,则替换堆顶并重新调整堆
- 最后对堆中的元素进行排序
2.2 函数签名
cpp
template<typename RandomIt>
void partial_sort(RandomIt first, RandomIt middle, RandomIt last);
template<typename RandomIt, typename Compare>
void partial_sort(RandomIt first, RandomIt middle, RandomIt last, Compare comp);
2.3 特点
- 不稳定排序:相等元素的相对顺序可能会改变
- 原地排序:不需要额外的存储空间
- 时间复杂度:O(n log k),其中 k 是要排序的元素个数(即 middle - first)
- 空间复杂度:O(1),不需要额外空间
2.4 应用举例
例1:获取前N个最小元素
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {5, 2, 9, 1, 5, 6, 3, 8, 7, 4};
// 排序前5个元素
std::partial_sort(v.begin(), v.begin() + 5, v.end());
std::cout << "First 5 sorted elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << v[i] << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;
std::cout << "Remaining elements: ";
for (int i = 5; i < v.size(); ++i) {
std::cout << v[i] << " "; // 输出: 9 6 8 7 5
}
std::cout << std::endl;
return 0;
}
例2:获取前N个最大元素
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {5, 2, 9, 1, 5, 6, 3, 8, 7, 4};
// 排序前5个最大元素
std::partial_sort(v.begin(), v.begin() + 5, v.end(), std::greater<int>());
std::cout << "First 5 largest elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << v[i] << " "; // 输出: 9 8 7 6 5
}
std::cout << std::endl;
return 0;
}
3. stable_sort 算法
3.1 算法原理
stable_sort 算法是一种稳定的排序算法,它保证相等元素的相对顺序不变。通常实现为归并排序:
- 将序列递归地分成两半
- 对每一半进行排序
- 将排序后的两半合并成一个有序序列
3.2 函数签名
cpp
template<typename RandomIt>
void stable_sort(RandomIt first, RandomIt last);
template<typename RandomIt, typename Compare>
void stable_sort(RandomIt first, RandomIt last, Compare comp);
3.3 特点
- 稳定排序:相等元素的相对顺序保持不变
- 非原地排序:需要额外的存储空间
- 时间复杂度:平均 O(n log n),最坏 O(n log n)
- 空间复杂度:O(n),需要额外的存储空间用于归并
3.4 应用举例
例1:基本用法
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {5, 2, 9, 1, 5, 6};
std::stable_sort(v.begin(), v.end());
std::cout << "Stably sorted: ";
for (int num : v) {
std::cout << num << " "; // 输出: 1 2 5 5 6 9
}
std::cout << std::endl;
return 0;
}
例2:稳定排序的重要性
cpp
#include <iostream>
#include <vector>
#include <algorithm>
struct Person {
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {}
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 25},
{"David", 30}
};
// 先按年龄排序
std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.age < b.age;
});
std::cout << "After sort (unstable):" << std::endl;
for (const auto& p : people) {
std::cout << p.name << " (" << p.age << ")" << std::endl;
}
// 重新初始化
people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 25},
{"David", 30}
};
// 用stable_sort按年龄排序
std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.age < b.age;
});
std::cout << "\nAfter stable_sort:" << std::endl;
for (const auto& p : people) {
std::cout << p.name << " (" << p.age << ")" << std::endl;
}
return 0;
}
4. 三种排序算法的性能比较
4.1 测试环境
- 硬件:Intel Core i5 处理器,8GB 内存
- 软件:Visual Studio 2022,C++17
- 数据规模:100,000 个随机整数
4.2 测试代码
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
void test_sort() {
const int size = 100000;
std::vector<int> v(size);
// 生成随机数据
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 1000000);
for (int i = 0; i < size; ++i) {
v[i] = dist(gen);
}
// 测试 sort
std::vector<int> v1 = v;
auto start = std::chrono::high_resolution_clock::now();
std::sort(v1.begin(), v1.end());
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "sort: " << duration.count() << " ms" << std::endl;
// 测试 partial_sort (前50000个元素)
std::vector<int> v2 = v;
start = std::chrono::high_resolution_clock::now();
std::partial_sort(v2.begin(), v2.begin() + 50000, v2.end());
end = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "partial_sort: " << duration.count() << " ms" << std::endl;
// 测试 stable_sort
std::vector<int> v3 = v;
start = std::chrono::high_resolution_clock::now();
std::stable_sort(v3.begin(), v3.end());
end = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "stable_sort: " << duration.count() << " ms" << std::endl;
}
int main() {
std::cout << "Testing sorting algorithms with 100,000 elements..." << std::endl;
test_sort();
return 0;
}
4.3 测试结果
| 排序算法 | 平均运行时间 (ms) | 特点 |
|---|---|---|
| sort | 15-25 | 不稳定,原地排序,平均 O(n log n) |
| partial_sort (前50%) | 10-18 | 不稳定,原地排序,O(n log k) |
| stable_sort | 20-30 | 稳定,需要额外空间,O(n log n) |
4.4 性能分析
-
sort:
- 最快的通用排序算法
- 适用于需要完整排序的场景
- 空间开销小
-
partial_sort:
- 当只需要前 N 个元素有序时,比完整排序更快
- 特别适用于 Top-K 问题
- 时间复杂度与 K 有关,K 越小,性能越好
-
stable_sort:
- 当需要保持相等元素的相对顺序时使用
- 由于需要额外的存储空间,通常比 sort 慢
- 适用于对包含多个字段的对象进行排序的场景
5. 适用场景总结
| 算法 | 适用场景 | 不适用场景 |
|---|---|---|
| sort | 大多数排序场景,对稳定性无要求 | 需要保持元素相对顺序的场景 |
| partial_sort | Top-K 问题,只需要部分元素有序 | 需要完整排序的场景 |
| stable_sort | 需要保持相等元素相对顺序的场景 | 对空间使用有严格限制的场景 |
6. 代码优化建议
-
选择合适的排序算法:
- 如果只需要前 N 个元素有序,使用
partial_sort - 如果需要保持元素相对顺序,使用
stable_sort - 其他情况,使用
sort
- 如果只需要前 N 个元素有序,使用
-
优化比较函数:
- 尽量使用简单的比较函数
- 对于复杂对象,考虑使用成员函数或 lambda 表达式
-
数据预处理:
- 对于大规模数据,可以考虑先进行数据清洗或预处理
- 对于几乎有序的数据,
stable_sort可能表现更好
-
内存管理:
- 对于
stable_sort,确保有足够的内存空间 - 对于大规模数据,可以考虑分块处理
- 对于
7. 结论
STL 提供了多种排序算法,每种算法都有其特定的适用场景:
sort是最通用、最高效的排序算法,适用于大多数场景partial_sort适用于只需要部分元素有序的场景,如 Top-K 问题stable_sort适用于需要保持相等元素相对顺序的场景
选择合适的排序算法可以显著提高程序的性能和正确性,根据具体的应用场景和需求选择最合适的排序方法是每个 C++ 开发者应该掌握的技能。