冒泡排序正确循环边界应为外层i从0到n-2、内层j从0到n-2-i;常见错误是i或j上限写成n-1导致越界或冗余比较。

冒泡排序的正确循环边界怎么写
错在 i 和 j 的上限——很多人写成 for (int i = 0; i 再套 <code>for (int j = 0; j ,结果多跑一轮、越界访问或比较无效位置。
核心逻辑是:每轮把最大值“冒”到末尾,后续轮次就不该再碰它。所以外层控制轮数(最多 n-1 轮),内层控制比较范围(每轮减少一个位置):
- 外层
i从0到n - 2(含),即i - 内层
j从0到n - 2 - i(含),即j - 这样第
i轮只比较前n - i个元素,末尾i个已就位
如何提前退出优化性能
原生冒泡最怕“已经排好序却还要扫完所有轮次”。加个标志位就能在无交换时立刻停住,对近乎有序数据效果明显。
但注意:这个优化不改变最坏时间复杂度(仍是 O(n²)),只是让最好情况从 O(n²) 降到 O(n):
立即学习“C++免费学习笔记(深入)”;
- 每轮开始前设
bool swapped = false - 只要发生一次
swap,就把swapped设为true - 本轮结束若
swapped == false,break外层循环 - 别漏掉初始化位置——必须在每轮开头重置,不是函数开头只设一次
用 std::swap 还是手写交换
手写临时变量交换(int t = a; a = b; b = t;)看似简单,但遇到自定义类型、引用、const 限定等情况容易出错;而 std::swap 是泛型且特化过的,安全又高效。
- 头文件要加
#include <utility>(C++11 起) - 对内置类型,编译器通常会内联优化,性能没差别
- 对类类型,
std::swap可能调用移动语义,比手写拷贝快得多 - 别用
using std::swap;后直接swap(a, b)就完事——得确保 ADL 正确生效,稳妥起见还是写全std::swap(a, b)
数组 vs std::vector 下标越界风险
用裸数组时,arr[j+1] 在 j == n-1 时必然越界;用 std::vector 看似有 at() 检查,但默认下标运算符 [] 同样不检查——和数组一样危险。
- 无论哪种容器,只要用了
j+1,内层循环上限就必须是n - 1(不是n) -
std::vector::at()会抛std::out_of_range,但异常开销大,不适合热路径 - 真要调试,可临时改用
at()快速定位越界点,上线前换回[] - 别依赖 IDE 或编译器警告来发现这类问题——它们不一定报
边界条件和提前退出是实际写对冒泡的关键,不是背熟模板就行。尤其当数组长度为 0 或 1 时,循环条件是否自然跳过、std::swap 是否仍安全、空 vector 的 size() 返回 0 是否导致 j 这种隐式转换问题——这些地方最容易在测试里漏掉。










