降序冒泡排序的关键是将比较条件改为a[i] < a[i+1],满足时交换相邻元素,使较大数逐轮前移,最终实现从大到小排列。

冒泡排序降序实现的关键在比较条件
升序时用 a[i] > a[i+1] 交换,降序只需反过来:只要 a[i] 就交换。这不是“改个符号”那么简单——它决定了整个排序方向,且直接影响后续自定义逻辑的合理性。
常见错误是只改了循环或变量名,却漏掉这个核心判断,结果跑出来还是升序,甚至越排越乱。
- 务必检查内层循环中交换发生的条件表达式
- 测试用例至少包含重复元素(如
{5,5,3,8,3}),确认相等时不交换,保证稳定性 - 数组边界要控制好,
i+1不能越界,推荐用size - 1 - pass做上界
用函数对象(functor)封装降序比较逻辑
把 a[i] 抽成可复用、可传参的比较逻辑,比硬编码更灵活。C++ 中最轻量的方式是定义一个带 <code>operator() 的结构体:
struct Greater {
bool operator()(int a, int b) const { return a < b; }
};这样后续换成 string 或自定义类型时,只要重载这个 operator(),排序主逻辑完全不用动。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 不要用普通函数指针替代 functor——泛型场景下无法推导模板参数
- 如果用
std::function<bool></bool>,会带来虚调用开销,对小数组不划算 - lambda 也可行,但捕获列表为空时,其类型无法作为模板参数直接传递给泛型排序函数
模板化冒泡排序 + 自定义比较器的完整写法
真正实用的版本必须支持任意容器和比较方式。核心是把比较器作为模板参数传入,而不是写死类型:
template<typename Iter, typename Compare>
void bubble_sort(Iter first, Iter last, Compare comp) {
for (auto i = first; i != last; ++i) {
bool swapped = false;
for (auto j = first; j < last - 1 - (i - first); ++j) {
if (comp(*(j + 1), *j)) { // 注意:这里是 comp(后, 前),实现降序
std::swap(*j, *(j + 1));
swapped = true;
}
}
if (!swapped) break;
}
}调用时传入 Greater{} 或 std::greater<int>{} 即可降序;传 std::less<int>{} 就变升序。
-
comp(后, 前)这个顺序容易写反——它对应“是否要把后面的元素往前挪”,降序时后面更大才该前移 - 迭代器版本比原始数组更通用,能适配
std::vector、std::array甚至原生指针 - 提前退出(
swapped标志)在已部分有序时显著提升性能,别省略
自定义类型排序时,operator< 不等于降序逻辑
很多人以为只要类里重载了 operator<,再传 std::greater<T>{} 就能降序——这是错的。因为 std::greater 默认调用 operator>,而你很可能根本没定义它。
正确做法是:要么显式定义 operator>,要么统一用 std::less + 自定义比较器返回 a.val > b.val。
- 避免依赖未定义行为:没声明
operator>就用std::greater,编译可能过,运行时行为未定义 - 更稳妥的是始终传 lambda:
[ ](const Person& a, const Person& b) { return a.age > b.age; } - 如果类型本身语义上“小”就是“优先”,那升序才是自然顺序,强行降序反而增加理解成本
实际写的时候,最容易被忽略的是比较器参数顺序和迭代器边界计算——这两处一错,排序结果既不正确也不稳定,还很难调试。

















