题目
编写程序,设计学生考试成绩记录(学号、姓名、总分)。对比 std::sort 与 std::stable_sort 的行为差异:验证当仅根据总分进行单级排序时,不稳定排序 std::sort 可能打乱同分考生的原始输入登记顺序;而 std::stable_sort(基于归并排序)绝对保证等价值元素的相对次序保持不变。
输入格式:无需外部输入。
输出格式:比对稳定排序与不稳定排序在同分数据上的先后排位次序。
数据范围与约定:算法稳定性、归并排序开销与多级复合规则。
思路与算法
1. 排序稳定性的定义
若输入序列中存在两个相同键值的元素 \(A\) 和 \(B\),且在原序列中 \(A\) 处于 \(B\) 前面;若排序后 \(A\) 依然严格位于 \(B\) 前面,则该排序算法是“稳定”的(Stable)。
2. 底层算法开销std::sort 采用内省快排,空间复杂度为 O(log N),平均时间 O(N log N) 但不稳定;std::stable_sort 采用自适应归并排序,若内存充足时消耗 O(N) 缓冲区保证线性对数时间,若内存不足则退化为 O(N (log N)^2) 原地归并。
3. 多级排序与稳定排序的关系
有了稳定排序,我们可以先对次要关键字(如姓名)排一次,再对主要关键字(如成绩)排一次,自然达成多级排序效果。
完整程序
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct Candidate {
int id;
std::string name;
int score;
};
void printCandidates(const std::vector<Candidate>& list, const std::string& label) {
std::cout << "=== " << label << " ===\n";
for (const auto& c : list) {
std::cout << "工号: " << c.id << " \t姓名: " << c.name << " \t成绩: " << c.score << "\n";
}
std::cout << "\n";
}
int main() {
// 原始序列中,同分 85 的先后顺序为:张三 (101) -> 王五 (103) -> 钱七 (105)
std::vector<Candidate> data = {
{101, "张三", 85},
{102, "李四", 95},
{103, "王五", 85},
{104, "赵六", 90},
{105, "钱七", 85}
};
auto dataSort = data;
auto dataStable = data;
// 仅按成绩从高到低降序
auto scoreDesc = [](const Candidate& a, const Candidate& b) {
return a.score > b.score;
};
// 1. std::sort (可能打乱同分顺序)
std::sort(dataSort.begin(), dataSort.end(), scoreDesc);
printCandidates(dataSort, "std::sort 排序结果 (同分相对顺序可能无法保证)");
// 2. std::stable_sort (严格保持原录入相对顺序)
std::stable_sort(dataStable.begin(), dataStable.end(), scoreDesc);
printCandidates(dataStable, "std::stable_sort 稳定排序结果 (同分 85 分保持张三->王五->钱七)");
return 0;
}运行示例
输出:
=== std::sort 排序结果 (同分相对顺序可能无法保证) ===
工号: 102 姓名: 李四 成绩: 95
工号: 104 姓名: 赵六 成绩: 90
工号: 101 姓名: 张三 成绩: 85
工号: 103 姓名: 王五 成绩: 85
工号: 105 姓名: 钱七 成绩: 85
=== std::stable_sort 稳定排序结果 (同分 85 分保持张三->王五->钱七) ===
工号: 102 姓名: 李四 成绩: 95
工号: 104 姓名: 赵六 成绩: 90
工号: 101 姓名: 张三 成绩: 85
工号: 103 姓名: 王五 成绩: 85
工号: 105 姓名: 钱七 成绩: 85