冒泡排序解析
1. 面试话术
"冒泡排序是一种基础的比较排序算法。它的核心思想是相邻元素两两比较,如果顺序不对就交换,每一轮都会把当前未排序部分中最大的元素像气泡一样'浮'到末尾。
具体来说,我会用两层循环来实现:外层循环 控制总共需要进行多少轮冒泡,一个长度为 n 的数组,最多需要 n-1 轮;内层循环负责在每一轮中做相邻元素的比较和交换,并且每轮结束后,末尾已经有一个元素就位了,所以内层循环的范围可以逐渐缩小。
另外,我会加一个优化 :用一个布尔变量 swapped 来记录本轮有没有发生交换,如果一轮下来没有任何交换,说明数组已经有序了,可以提前退出,这样对近似有序的数组能把最好情况的时间复杂度优化到 O(n)。
2. Java 代码详解
java
public class BubbleSort {
public static void bubbleSort(int[] arr) {
// 先拿到数组长度,后面会频繁用到
int n = arr.length;
// ====== 外层循环 ======
// 外层循环控制"总共要冒几轮"
// 每轮结束,最大的元素就像气泡一样浮到了末尾
// n 个元素最多需要 n-1 轮,所以 i 从 0 到 n-2
for (int i = 0; i < n - 1; i++) {
// 【优化点】这个 flag 用来判断本轮有没有发生交换
// 如果本轮一次交换都没发生,说明数组已经完全有序
// 可以直接 break,不用再继续后面的轮次了
boolean swapped = false;
// ====== 内层循环 ======
// 内层循环负责在本轮中,对"未排序区间"做相邻比较
// 为什么上界是 n - 1 - i ?
// 因为经过第 i 轮之后,末尾 i 个元素已经排好了
// 它们是"已排序区",不需要再碰了,所以每轮少比较一次
for (int j = 0; j < n - 1 - i; j++) {
// 比较相邻的两个元素
// 如果前面的比后面的大,说明顺序错了,需要交换
// 这就是"冒泡"的核心动作:大的往右走
if (arr[j] > arr[j + 1]) {
// 经典三步换:借助临时变量 temp 完成交换
// 直接 arr[j] = arr[j+1] 会把 arr[j] 原来的值丢掉
// 所以必须先用 temp 把它存起来
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
// 发生了交换,把 flag 标记为 true
swapped = true;
}
}
// 本轮结束后检查 flag
// 如果 swapped 依然是 false,说明整轮没有任何交换
// 数组已经有序,直接提前退出,不用再浪费时间了
if (!swapped) {
break;
}
}
}
// ====== 主函数:验证一下效果 ======
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.print("排序前: ");
printArray(arr);
bubbleSort(arr);
System.out.print("排序后: ");
printArray(arr);
}
// 辅助函数:打印数组
static void printArray(int[] arr) {
for (int val : arr) {
System.out.print(val + " ");
}
System.out.println();
}
}
📌 运行结果
排序前: 64 34 25 12 22 11 90
排序后: 11 12 22 25 34 64 90
📌 手动模拟一遍(以 [5, 3, 1, 4] 为例)
| 轮次 | 过程 | 结果 |
|---|---|---|
| 第 1 轮 | (5,3)换→(5,1)换→(5,4)换 | [3, 1, 4, 5] → 5 到位 |
| 第 2 轮 | (3,1)换→(3,4)不换 | [1, 3, 4, 5] → 4 到位 |
| 第 3 轮 | (1,3)不换 → swapped=false | 提前退出 ✅ |
📌 关键变量总结
| 变量 | 类型 | 作用 |
|---|---|---|
n |
int | 数组长度 |
i |
int | 外层轮次计数,同时代表末尾已排好的元素个数 |
j |
int | 内层遍历下标,用来比较 arr[j] 和 arr[j+1] |
swapped |
boolean | 优化标志,本轮是否发生过交换 |
temp |
int | 交换时的临时变量,防止值被覆盖 |
📌 复杂度分析
| 情况 | 时间复杂度 | 说明 |
|---|---|---|
| 最坏情况 | O(n²) | 数组完全逆序,每轮都要交换到底 |
| 平均情况 | O(n²) | 两层循环嵌套决定的 |
| 最好情况 | O(n) | 数组已经有序,第一轮没有交换,swapped 优化直接退出 |
| 空间复杂度 | O(1) | 只用了 temp、swapped 等几个固定变量,原地排序 |
分析过程 :外层循环最多跑
n-1次,内层循环第i轮跑n-1-i次,总比较次数 =(n-1) + (n-2) + ... + 1= n(n-1)/2 ,所以是 O(n²)。
📌 冒泡排序的稳定性
冒泡排序是稳定排序
因为我们的判断条件是 arr[j] > arr[j+1],严格大于才交换,相等的元素不会交换位置,所以相对顺序保持不变。