冒泡排序
冒泡排序是排序算法的一种,通过比较和交换两个相邻的元素,直到按照想要的顺序(从小到大或从大到小)为止。简言之:两两排序。
const bubbleSort = (array) => {
const size = array.length;
for(let i = 0; i < size - 1; i++) {
for (let j = 0; j < size- 1; j++) {
if (array[j] > array[j + 1]) {
[array[j], array[j + 1]] = [array[j + 1], array[j]];
}
}
}
return array;
};
冒泡排序的工作原理
假如我们将要将元素升序排序。
-
第一次迭代(比较和交换)
1.1 从第一个索引开始,比较第一个和第二个元素
1.2 如果第一个元素比第二个元素大,交换它们
1.3 比较第二个和第三个元素,如果第二个比第三个大,交换它们
1.4 重复以上步骤,直到最后一个元素。
- 其余的迭代
剩余的迭代同第一次迭代步骤一样。
每次迭代后,未排序元素中最大元素被放在末尾。
每一次迭代都会进行比较,直到最后一个未排序的元素为止。
当所有未排序的元素都放在正确的位置时,这个数组便是有序的了。
冒泡排序的代码
const bubbleSort = (array) => {
const size = array.length;
for(let i = 0; i < size - 1; i++) {
for (let j = 0; j < size- 1; j++) {
if (array[j] > array[j + 1]) {
[array[j], array[j + 1]] = [array[j + 1], array[j]];
}
}
}
return array;
};
冒泡排序优化
我们仔细观察上面的代码可以发现,即使数组已经是有序的情况下,依然会进行比较。这增加了代码的执行时间。
这里我们可以进行优化:引入一个变量 swapped,如果元素进行了交换,设置它的值为 true,否则为 false。
如果一轮迭代结束后,没有交换, swapped 的值是 fasle, 意味着元素已经是有序的了,就不需要再执行其他的迭代了。
优化后的冒泡排序代码
const bubbleSort = (array) => {
let size = array.length;
for (let i = 0; i < size - 1; i++) {
let swapped = false;
for (let j = 0; j < size - 1; j++) {
if (array[j] > array[j + 1]) {
//
[array[j], array[j + 1]] = [array[j + 1], array[j]];
swapped = true;
}
}
if (!swapped) break;
}
};
复杂度分析:
| 名字 | 最好 | 平均 | 最坏 | 内存 | 是否稳定 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 是 |
复杂度分析详解
时间复杂度
冒泡排序对比相邻的元素:
| 每一轮 | 对比的次数 | |
|---|---|---|
| 第一轮迭代 | (n - 1) | |
| 第二轮迭代 | (n - 2) | |
| ...... | ...... | |
| 最后一轮迭代 | ---- | 1 |
因此,对比的次数为:
(n-1) + (n-2) + (n-3) + ...... + 1 = n(n-1)/2
结果相当于 n^2,因此,时间复杂度为 O(n^2)
我们也可以通过观察代码,冒泡排序经过两轮循环,因此,时间复杂度为 O(n^2)。
空间复杂度
- 空间复杂度为
O(1),因为需要一个额外的变量用于交换 - 优化过后的空间复杂度为
O(2),因为多用了一个额外变量。
注: JavaScript 中使用解构赋值的方式进行交换,我不清楚底层代码如何实现的,如果有大佬知道可以留言,欢迎探讨。