外层循环上限为n-1,内层上限为n-1-i;i从0开始控制轮数,j从0开始遍历未排序部分,每轮后最大元素沉底,边界动态收缩。

冒泡排序的正确循环边界怎么写
写错 i 或 j 的上限是初学者最常触发越界或漏排序的原因。外层循环控制轮数,最多 n-1 轮;内层比较相邻元素,每轮末尾的元素已就位,所以边界要动态收缩。
- 外层用
for (int i = 0; i ,不是 <code> —— 第 <code>n轮没意义 - 内层用
for (int j = 0; j ,不是 <code> —— 否则每次都会访问到已排好序的末尾位置,可能越界(尤其当 <code>n == 1) - 如果用
std::vector,记得用.size()并转成int,否则size_t和负数相减会绕回极大值,直接崩
swap 函数该不该手写
用 std::swap 是安全且高效的默认选择,手写容易出错,尤其涉及指针、自定义类型或移动语义时。
- 基础类型(
int、double)用std::swap没开销,编译器会优化成寄存器交换 - 别写三变量交换(
tmp = a; a = b; b = tmp;),既啰嗦又掩盖了意图;更别用异或技巧(a ^= b ^= a ^= b),对非整型不适用,且违反序列点规则,c++17 后可能未定义行为 - 如果容器元素是自定义类,确保它支持移动或拷贝——
std::swap会自动优选移动构造/赋值,比手写更鲁棒
提前退出(优化版)加不加判断
加,但只在确定数据可能部分有序时才值得。纯教学或小数组(n )可省略,否则空跑 <code>n² 次毫无必要。
- 声明一个
bool swapped = false,每次成功交换就置true - 内层循环结束后检查:若
!swapped,直接break外层循环 - 注意:这个优化不影响最坏时间复杂度(仍是
O(n²)),但最好情况从O(n²)降到O(n) - 别在每轮都清零
swapped后立刻设为false—— 顺序错了会导致提前退出失效
vector 和 int[] 的传参差异
传 std::vector 默认是值拷贝,排序函数里改的是副本,原数组不变;而裸数组传参本质是传指针,改的是原内存。
立即学习“C++免费学习笔记(深入)”;
- 想原地排序
vector,必须用引用:参数写成std::vector<int>& arr</int> - 裸数组如
int arr[10],函数参数写int arr[]或int* arr效果一样,但无法在函数内用sizeof(arr)得到长度,得额外传size - 混用时容易踩坑:比如把
vector.data()传给期望int*的函数,没问题;但反过来把&arr[0]传给期望std::vector&的函数,编译不过
冒泡排序本身逻辑简单,但边界、传参、交换方式这些细节一旦写错,调试时现象往往很隐蔽:有时少排一个数,有时程序卡死,有时只在 Release 模式下出错。真正要注意的不是“怎么写出来”,而是“为什么这里必须这么写”。