用C++解决‘合影效果’排序题:从冒泡到STL sort的三种实战思路(附完整代码)
·
用C++解决‘合影效果’排序题:从冒泡到STL sort的三种实战思路(附完整代码)
想象一下这样的场景:班级合影时,老师要求男生按身高从低到高站在前排,女生按身高从高到低站在后排。这个看似简单的排队问题,实际上蕴含着排序算法的精髓。今天我们就以这道经典的OJ题目为例,带你领略C++中排序算法的多样魅力。
1. 问题分析与基础解法
合影效果问题本质上是一个多条件排序问题。我们需要处理两类数据:男生身高(升序)和女生身高(降序)。对于初学者来说,最直观的解法就是将两类数据分开处理。
1.1 分离数据法
这种方法的核心思想是将输入数据按性别分流到两个不同的数组:
double male[45], female[45];
int mi = 0, fi = 0; // 计数器初始化
// 数据输入分流
for(int i = 0; i < n; ++i) {
char gender[10];
double height;
cin >> gender >> height;
if(gender[0] == 'm') {
male[mi++] = height; // 男生数据
} else {
female[fi++] = height; // 女生数据
}
}
1.2 分别排序与输出
分离数据后,我们可以对两个数组分别排序:
// 男生升序排序
sort(male, male + mi);
// 女生降序排序
sort(female, female + fi, greater<double>());
// 输出结果
for(int i = 0; i < mi; ++i)
cout << fixed << setprecision(2) << male[i] << ' ';
for(int i = 0; i < fi; ++i)
cout << fixed << setprecision(2) << female[i] << ' ';
提示:使用
greater<double>()可以方便地实现降序排序,这比自定义比较函数更简洁。
2. 进阶解法:统一比较条件
当掌握了基础解法后,我们可以尝试更优雅的解决方案——通过自定义比较条件实现统一排序。
2.1 结构体定义
首先定义一个包含性别和身高的结构体:
struct Student {
char gender[15];
double height;
};
2.2 自定义比较函数
关键点在于设计一个能够处理所有情况的比较函数:
bool compare(const Student &a, const Student &b) {
if(a.gender[0] == 'm' && b.gender[0] == 'm') {
return a.height < b.height; // 男生升序
} else if(a.gender[0] == 'f' && b.gender[0] == 'f') {
return a.height > b.height; // 女生降序
} else {
return a.gender[0] == 'm'; // 男生在前
}
}
2.3 运算符重载实现
更C++风格的做法是重载小于运算符:
struct Student {
char gender[15];
double height;
bool operator<(const Student &other) const {
if(gender[0] == 'm' && other.gender[0] == 'm') {
return height < other.height;
} else if(gender[0] == 'f' && other.gender[0] == 'f') {
return height > other.height;
} else {
return gender[0] == 'm';
}
}
};
3. 手动实现排序算法
为了深入理解排序原理,我们尝试手动实现两种经典排序算法来解决这个问题。
3.1 冒泡排序实现
冒泡排序虽然效率不高,但实现简单,适合教学演示:
void bubbleSort(Student arr[], int n) {
for(int i = 0; i < n-1; ++i) {
for(int j = 0; j < n-i-1; ++j) {
// 自定义比较条件
bool shouldSwap = false;
if(arr[j].gender[0] == 'm' && arr[j+1].gender[0] == 'm') {
shouldSwap = arr[j].height > arr[j+1].height;
} else if(arr[j].gender[0] == 'f' && arr[j+1].gender[0] == 'f') {
shouldSwap = arr[j].height < arr[j+1].height;
} else if(arr[j].gender[0] == 'f' && arr[j+1].gender[0] == 'm') {
shouldSwap = true;
}
if(shouldSwap) {
swap(arr[j], arr[j+1]);
}
}
}
}
3.2 插入排序实现
插入排序在小数据量时表现良好:
void insertionSort(Student arr[], int n) {
for(int i = 1; i < n; ++i) {
Student key = arr[i];
int j = i - 1;
while(j >= 0 &&
((arr[j].gender[0] == 'm' && key.gender[0] == 'm' && arr[j].height > key.height) ||
(arr[j].gender[0] == 'f' && key.gender[0] == 'f' && arr[j].height < key.height) ||
(arr[j].gender[0] == 'f' && key.gender[0] == 'm'))) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}
4. 性能对比与选择建议
虽然题目数据量很小(n≤40),但了解不同方法的性能特点仍然很有价值。
4.1 时间复杂度分析
| 方法 | 最好情况 | 平均情况 | 最坏情况 |
|---|---|---|---|
| STL sort | O(n log n) | O(n log n) | O(n log n) |
| 冒泡排序 | O(n) | O(n²) | O(n²) |
| 插入排序 | O(n) | O(n²) | O(n²) |
4.2 实际应用选择
- 竞赛场景 :优先使用STL sort+自定义比较,代码简洁高效
- 教学目的 :手动实现排序算法有助于理解原理
- 特殊需求 :如果需要稳定排序,可以考虑stable_sort
4.3 代码风格建议
- 变量命名 :使用有意义的名称(如studentList而非简单的arr)
- 函数封装 :将排序逻辑封装成独立函数
- 注释清晰 :特别是复杂比较逻辑处
- 错误处理 :添加基本的输入验证
// 良好的代码风格示例
struct PhotoMember {
enum Gender { MALE, FEMALE };
Gender gender;
double height;
bool shouldStandBefore(const PhotoMember &other) const {
if(gender == MALE && other.gender == MALE) {
return height < other.height;
} else if(gender == FEMALE && other.gender == FEMALE) {
return height > other.height;
} else {
return gender == MALE;
}
}
};
在实际教学中发现,学生最容易混淆的是比较条件的逻辑判断。建议先用自然语言明确规则,再转换为代码。例如:"男生在前,女生在后;同性别时,男生矮在前,女生高在前"。
更多推荐



所有评论(0)