冒泡排序¶
一、算法描述¶
1.1 核心思想¶
- 每次操作只会影响相邻的 2 个元素
- 每轮冒泡至少让 1 个元素移动到它正确的位置(1 轮是指从头到尾操作一遍元素)
- 最多经过 n 轮冒泡后,所有元素都会被移到正确的位置
1.2 细节解释¶
比如说,数组 [2, 7, 1, 4, 3, 6, 5]。
___ ___ ___ ___ ___ ___ ___
| 2 | 7 | 1 | 4 | 3 | 6 | 5 |
从后面往前面冒泡,由于 5 < 6 所以 5 要往前冒泡:
___
| |
V V
___ ___ ___ ___ ___ ___ ___
| 2 | 7 | 1 | 4 | 3 | 5 | 6 |
接着判断 5 > 3,5 不能往前冒泡,当前对象换成 3:
|
V
___ ___ ___ ___ ___ ___ ___
| 2 | 7 | 1 | 4 | 3 | 5 | 6 |
接下来是 3 < 4,所以 3 要往前冒泡:
___
| |
V V
___ ___ ___ ___ ___ ___ ___
| 2 | 7 | 1 | 3 | 4 | 5 | 6 |
再接着是 3 > 1,3 不能冒泡,切换当前对象为 1:
|
V
___ ___ ___ ___ ___ ___ ___
| 2 | 7 | 1 | 3 | 4 | 5 | 6 |
接下来是 1 < 7,1 要往前冒泡:
___
| |
V V
___ ___ ___ ___ ___ ___ ___
| 2 | 1 | 7 | 3 | 4 | 5 | 6 |
最后是 1 < 2,所以 1 要往前冒泡:
___
| |
V V
___ ___ ___ ___ ___ ___ ___
| 1 | 2 | 7 | 3 | 4 | 5 | 6 |
这样一轮冒泡结束后,1 已经在它应该在的位置了。
已排序 未排序
___ ___ ___ ___ ___ ___ ___
| 1 | | 2 | 7 | 3 | 4 | 5 | 6 |
后面再对其他未排序的元素进行冒泡,直到所有元素都排好序为止。
每过一轮冒泡,总能把一个值放到它正确的位置,经过 n 轮后就排好序了。
二、算法实现¶
- 总共执行 n 轮冒泡
- 每轮冒泡,会将未排序元素的最小值放到已排序元素的后面
public void bubbleSort(int[] arr) {
int n = arr.length;
// 进行 n 轮冒泡
for (int i = 0; i < n; i++) {
// 每轮冒泡,都会将一个元素放置到正确的位置
for (int j = n - 1; j > i; j--) {
// 每次只会操作相邻的2个元素
if (arr[j] < arr[j - 1]) {
// 交换相邻元素
swap(arr, j, j - 1);
}
}
}
}
举个例子,原始数据是:[0, 3, 1, 6, 2, 5, 4],冒泡排序过程如下:
=====第 1 轮冒泡=====
[0, 3, 1, 6, 2, 4, 5]
[0, 3, 1, 6, 2, 4, 5]
[0, 3, 1, 2, 6, 4, 5]
[0, 3, 1, 2, 6, 4, 5]
[0, 1, 3, 2, 6, 4, 5]
[0, 1, 3, 2, 6, 4, 5]
=====第 2 轮冒泡=====
[0, 1, 3, 2, 6, 4, 5]
[0, 1, 3, 2, 4, 6, 5]
[0, 1, 3, 2, 4, 6, 5]
[0, 1, 2, 3, 4, 6, 5]
[0, 1, 2, 3, 4, 6, 5]
=====第 3 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
=====第 4 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
=====第 5 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
=====第 6 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
三、算法优化¶
当某轮冒泡中,没有执行任何交换时,其实就说明数组已经排序好了。
遇到这种情况,可以对冒泡排序进行优化,提前跳出循环:
public void bubbleSort(int[] arr) {
int n = arr.length;
// 进行 n 轮冒泡
for (int i = 0; i < n; i++) {
// 每轮冒泡,都会将一个元素放置到正确的位置
boolean flag = false;
for (int j = n - 1; j > i; j--) {
// 每次只会操作相邻的2个元素
if (arr[j] < arr[j - 1]) {
// 交换相邻元素
swap(arr, j, j - 1);
flag = true;
}
}
// 一轮冒泡里没有交换,说明已经排好序了
if (!flag) {
break;
}
}
}
上面同样的例子,执行过程如下:
=====第 1 轮冒泡=====
[0, 3, 1, 6, 2, 4, 5]
[0, 3, 1, 6, 2, 4, 5]
[0, 3, 1, 2, 6, 4, 5]
[0, 3, 1, 2, 6, 4, 5]
[0, 1, 3, 2, 6, 4, 5]
[0, 1, 3, 2, 6, 4, 5]
=====第 2 轮冒泡=====
[0, 1, 3, 2, 6, 4, 5]
[0, 1, 3, 2, 4, 6, 5]
[0, 1, 3, 2, 4, 6, 5]
[0, 1, 2, 3, 4, 6, 5]
[0, 1, 2, 3, 4, 6, 5]
=====第 3 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
=====第 4 轮冒泡=====
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
四、算法分析¶
4.1 时间复杂度¶
- 最好时间复杂度:O(n)
- 最坏时间复杂度:O(n^2)
- 平均时间复杂度:O(n^2)
4.2 空间复杂度¶
- 空间复杂度:O(1)
- 原地算法
4.3 稳定性¶
- 稳定排序算法
附录¶
/**
* 冒泡排序
* <p>
* 时间复杂度:最好 O(n) 最差 O(n^2) 平均 O(n^2)
* <p>
* 空间复杂度:O(1)
* <p>
* 稳定性:稳定
*
* @author weijiaduo
* @since 2022/7/21
*/
public class BubbleSort implements Sort {
/**
* 排序
*
* @param arr 数组
*/
@Override
public void sort(int[] arr) {
// slowSort(arr);
fastSort(arr);
}
/**
* 慢一点的冒泡排序
*
* @param arr 数组
*/
private void slowSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
for (int j = n - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
swap(arr, j, j - 1);
}
}
}
}
/**
* 快一点的冒泡排序
*
* @param arr 数组
*/
private void fastSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
boolean flag = false;
for (int j = n - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
swap(arr, j, j - 1);
flag = true;
}
}
// 一轮冒泡里没有交换,说明已经排好序了
if (!flag) {
break;
}
}
}
}