1. 冒泡排序过程
- 假设,我们现在要将这个无序数组
[1,5,3,2,6]从小到大来排列
按冒泡排序的思想:
要把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置;当一个元素小于或等于右侧相邻元素时,位置不变
第一轮:

第二轮:

第三轮:

第四轮

| 轮数 | 对比次数 | 确认元素个数 | 有序区个数 |
|---|---|---|---|
| 第1轮 | 4 | 1 | 1 |
| 第2轮 | 3 | 1 | 2 |
| 第3轮 | 2 | 1 | 3 |
| 第4轮 | 1 | 1 | 5 |
| 结论: |
- 元素交换轮数
=数组长度-1 - 每一轮交换次数
=数组长度-当前交换轮
代码实现思路
- 我们可以用 for 循环嵌套来实现,外部循环控制交换轮数
- 内部循环用来实现每一轮的交换处理。先进行元素比较,如果元素大于右侧相邻相元素,则两元素位交换,如果不大于,则啥也不做
js
// 排序数组
var arr = [1, 5, 3, 2, 6];
// 数组长度
var len = arr.length;
// 外层for控制交换轮数
for (var i = 0; i < len - 1; i++) {
// 内层for控制每一轮,元素交换次数处理
for (var j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换两元素位置
var tmp; // 用来交换两个变量的中间变量
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
console.log(arr); // [1, 2, 3, 5, 6]
2. 冒泡排序优化1 --减少没必要的"轮次"
假设法:
- 如果当前数组以及该是有序,它压根就不会进到交换
- 假设数组一开始就是有序的,如果从未进入
if
// 交换两元素位置
var tmp; // 用来交换两个变量的中间变量
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
代表他是有序的
- 每一轮循环时假设他是有序的 如果确实没进入交换代表是有序的,后面就不要再交换了
在每一轮开始时,默认打上 isSorted='有序' 标记,如果在这一轮交换中,数据一旦发生交换,就把 isSorted='无序',如果整轮交换中,都没有发生交换,那就表示数组是有序的了。我们就可以退出整个 for 循环的执行。
js
// 排序数组
var arr = [1, 5, 3, 2, 6];
// 数组长度
var len = arr.length;
var isSorted;
// 外层for控制交换轮数
for (var i = 0; i < len - 1; i++) {
isSorted = true; // 假设当前数组是有序的
// 内层for控制每一轮,元素交换次数处理
for (var j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换两元素位置
var tmp; // 用来交换两个变量的中间变量
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
//如果当前数组以及该是有序,它压根就不会进到交换
isSorted = false;
}
}
if(isSorted) {
break;
}
}
console.log(arr); // [1, 2, 3, 5, 6]

优化的价值
- 最好情况(原本有序):
时间复杂度从 O(n²) → O(n) - 实际项目中,对"几乎有序"的数据非常友好
3. 冒泡排序优化 2 -- 减少没必要的"比较"
- 动态缩小"无序区"的右边界
- 记录每一轮最后一次交换元素的位置,该位置为无序列表的边界,再往右就是有序区了
- 每一轮比较,比较到上一轮元素最后一次交换的位置(即无序列表的边界)就不再比较了。
| 变量 | 解决的问题 |
|---|---|
| isSorted | 还要不要继续排序 |
| sortBorder | 内层循环跑到哪里为止 |
| index | 下一轮新的无序边界 |
ini
// 排序数组
var arr = [98, 2, 3, 45, 4, 1, 5, 78, 6, 7, 8, 20];
// 数组长度
var len = arr.length;
// 当前是否是有序的
var isSorted;
// 有序的边界
var sortBorder = len - 1; //初始值
// 记录每一轮最后一次交换的值,确定下一次循有序边界
var index;
// 外层for控制交换轮数
for (var i = 0; i < len - 1; i++) {
// 内层for控制每一轮,元素交换次数处理
isSorted = true; // 有序标记,每轮开始默认为有序,如果一旦发生交换,就会变成flag=false,无序
for (var j = 0; j < sortBorder; j++) {
if (arr[j] > arr[j + 1]) {
// 交换两元素位置
var tmp; // 用来交换两个变量的中间变量
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
isSorted = false;
// 把无序列表的边界,更新为最后一次交换元素的位置
index = j;
}
}
// 如果无序,记录上一次最后一次交换的元素下标
if (!isSorted) {
sortBorder = index;
}
// 这一轮多次交换下来,flag没有变为false,说明没有发生元素交换,此时数组已是有序的
if (isSorted) {
break; // 退出最外层for循环
}
}
console.log(arr);

- 尾部有序的数据越多,收益越大
- 对"部分有序、局部乱序"的数组特别友好
- 冒泡排序从"教学算法",进化成了一个还算能用的基础排序