文章目录
C++ 排序算法完全指南:从入门到实践
🌟 一、排序基础认知
排序的本质:把一群数据按照特定规则重新排列(比如从矮到高排队)
核心目标:让数据变得有序,方便后续快速查找和处理
关键指标:
- 🚀 速度:处理百万数据要快(时间复杂度)
- 🏠 内存:尽量少占内存(空间复杂度)
- 🤝 稳定性:相同元素保持原顺序(重要吗?看需求!)
二、常用排序方法详解
1️⃣ 标准库一键排序(推荐首选)
适用场景:日常开发中90%的情况
优点:简单高效,底层优化好
#include <algorithm>
#include <vector>
int main() {
std::vector<int> nums = {5, 3, 7, 1, 9};
// 默认升序(从小到大)
std::sort(nums.begin(), nums.end());
// 降序排列(从大到小)
std::sort(nums.begin(), nums.end(), std::greater<int>());
return 0;
}
📝 注意点:
- 适用于数组/vector等连续容器
- 对链表无效(链表要用list.sort())
- 默认不保持相同元素的原始顺序
2️⃣ 自定义排序规则
适用场景:需要特殊排序逻辑
示例需求:按绝对值排序、按学生成绩排序等
// 按字符串长度排序
std::vector<std::string> words = {"apple", "cat", "banana"};
std::sort(words.begin(), words.end(), [](const auto& a, const auto& b) {
return a.size() < b.size();
});
// 结果:cat → apple → banana
💡 经典案例:多条件排序
struct Student {
std::string name;
int score;
int age;
};
std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) {
// 先按成绩降序,成绩相同按年龄升序
if (a.score != b.score)
return a.score > b.score;
else
return a.age < b.age;
});
3️⃣ 结构体/对象排序
两种方式:
- 重载运算符(适合固定排序规则)
struct Book {
std::string title;
double price;
// 按价格升序
bool operator<(const Book& other) const {
return price < other.price;
}
};
- 自定义比较函数(灵活多变)
std::sort(books.begin(), books.end(), [](const Book& a, const Book& b) {
return a.title < b.title; // 按书名排序
});
4️⃣ 稳定排序(保持原顺序)
适用场景:需要多次排序时保留前次顺序
std::vector<std::pair<int, std::string>> records = {
{90, "张三"}, {85, "李四"}, {90, "王五"}
};
// 按分数排序,相同分数保持录入顺序
std::stable_sort(records.begin(), records.end(),
[](const auto& a, const auto& b) {
return a.first > b.first;
});
/* 结果:
90 张三 → 90 王五 → 85 李四
保持相同分数的原始录入顺序 */
5️⃣ 部分排序(Top K问题)
适用场景:找前N名/省时省内存
std::vector<int> scores = {78, 92, 65, 88, 95, 81};
int k = 3; // 找前三名
// 只排前3个元素,其他不管
std::partial_sort(scores.begin(), scores.begin()+k, scores.end(),
std::greater<int>());
// 结果:95 92 88 65 78 81
6️⃣ 堆排序(优先队列)
适用场景:实时获取最大值/最小值
#include <queue>
// 小顶堆(总弹出最小值)
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
// 大顶堆(默认,总弹出最大值)
std::priority_queue<int> max_heap;
// 示例:实时获取最新三个最高分
std::vector<int> scores = {85, 92, 78, 95, 88};
for (int score : scores) {
max_heap.push(score);
if (max_heap.size() > 3)
max_heap.pop();
}
// 最终堆中保留:95, 92, 88
7️⃣ 快速排序(手写实现)
核心思想:分而治之 + 基准值
实现步骤:
- 选基准(如中间值)
- 分区:小的放左边,大的放右边
- 递归处理左右子数组
void quickSort(int arr[], int left, int right) {
if (left >= right) return;
int pivot = arr[(left+right)/2]; // 基准值
int i = left, j = right;
while (i <= j) {
while (arr[i] < pivot) i++; // 找左边大的
while (arr[j] > pivot) j--; // 找右边小的
if (i <= j) {
std::swap(arr[i], arr[j]);
i++; j--;
}
}
quickSort(arr, left, j); // 排左边
quickSort(arr, i, right); // 排右边
}
三、基础排序算法(理解原理)
1️⃣ 选择排序
工作原理:每轮选最小/最大放前面
类比:打牌时每次挑最小的牌排列
void selectionSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
int min_idx = i;
for (int j = i+1; j < n; j++) {
if (arr[j] < arr[min_idx])
min_idx = j;
}
std::swap(arr[i], arr[min_idx]);
}
}
2️⃣ 冒泡排序
工作原理:相邻元素比较交换,像气泡上浮
优化点:添加标记提前终止
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
bool swapped = false;
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
std::swap(arr[j], arr[j+1]);
swapped = true;
}
}
if (!swapped) break; // 已有序则提前退出
}
}
3️⃣ 插入排序
工作原理:像整理扑克牌,逐个插入合适位置
优势:对基本有序数据效率极高
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i-1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j]; // 后移元素
j--;
}
arr[j+1] = key; // 插入正确位置
}
}
四、排序算法对比表
算法 | 平均时间复杂度 | 最坏情况 | 内存消耗 | 稳定性 | 适用场景 |
---|---|---|---|---|---|
std::sort | O(n log n) | O(n²) | O(log n) | 不稳定 | 通用场景 |
快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 内存排序 |
归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 大数据/稳定需求 |
堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | Top K问题 |
插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 小数据/基本有序 |
冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 教学示例 |
选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 教学示例 |
五、选择排序算法的黄金法则
- 默认选择:优先使用
std::sort
,经过高度优化 - 需要稳定:选
stable_sort
或归并排序 - 海量数据:考虑分治策略或外部排序
- 实时处理:使用堆排序维护Top N
- 小数据量(<100):插入排序更高效
- 链表排序:使用
list.sort()
(归并实现) - 并行处理:C++17起可用
std::execution::par
并行加速
六、性能优化技巧
- 减少拷贝:对大型对象使用指针或移动语义
- 预先分配内存:避免排序过程中的内存分配
- 使用视图:排序时只处理索引,不移动实际数据
- 避免虚函数:比较函数尽量简单高效
- 利用缓存:让相关数据在内存中连续存储
七、常见问题解答
Q:已经排序好的数据用什么算法最快?
A:插入排序,时间复杂度可达O(n)
Q:1亿个数据选哪种排序?
A:先用std::sort
,如果内存不足考虑分块排序+归并
Q:如何判断排序是否稳定?
A:看相同元素顺序是否改变,例如:
struct Item { int id; int value; };
std::vector<Item> items = {{1,5}, {2,5}, {3,5}};
// 稳定排序后id顺序保持1→2→3
八、实战练习建议
- 尝试手写不同排序算法
- 用大量随机数据测试各算法性能
- 实现多条件组合排序(如先按年龄再按分数)
- 处理自定义对象的排序
- 解决LeetCode排序相关题目(如#56合并区间)
掌握这些排序技巧,你就能轻松应对各种数据排列需求! 🚀
求三连~🐍🐍🐍