冒泡排序,英文称为:Bubble Sort。
该算法名称的由来是因为数据会经由交换慢慢“浮”到数列的顶端。
冒泡排序的基本思想
冒泡排序的基本思想是多次比较和交换:
(1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
(2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
(3)针对所有的元素重复以上的步骤,除了最后一个。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
冒泡排序示例
以从小到大排序为例:从左向右排序,小的在左边,大的在右边
初始数据:[67, 60, 55, 50, 45]
一轮排序:[60, 55, 50, 45, 67]
二轮排序:[55, 50, 45, 60, 67]
三轮排序:[50, 45, 55, 60, 67]
四轮排序:[45, 50, 55, 60, 67]
排序结果:[45, 50, 55, 60, 67]
(1)第 1 轮排序:从数组的头部开始向尾部依次排序。
首先比较 67 和 60 ,由于 67 比 60 大,两者需要交换位置,交换位置后 [60, 67, 55, 50, 45];
继续比较 67 和 55 ,由于 67 比 55 大,两者需要交换位置,交换位置后 [60, 55, 67, 50, 45];
继续比较 67 和 50 ,由于 67 比 50 大,两者需要交换位置,交换位置后 [60, 55, 50, 67, 45];
继续比较 67 和 50 ,由于 67 比 45 大,两者需要交换位置,交换位置后 [60, 55, 50, 45, 67];
(2)第 2 轮排序:从数组的头部开始向尾部依次排序。
首先比较 60 和 55 ,由于 60 比 55 大,两者需要交换位置,交换位置后 [55, 60, 50, 45, 67];
继续比较 60 和 50 ,由于 60 比 50 大,两者需要交换位置,交换位置后 [55, 50, 60, 45, 67];
继续比较 60 和 45 ,由于 60 比 45 大,两者需要交换位置,交换位置后 [55, 50, 45, 60, 67];
继续比较 60 和 67 ,由于 60 比 67 小,两者不需要交换位置;
(3)第 3 轮排序:从数组的头部开始向尾部依次排序。
首先比较 55 和 50 ,由于 55 比 50 大,两者需要交换位置,交换位置后 [50, 55, 45, 60, 67];
继续比较 55 和 45 ,由于 55 比 45 大,两者需要交换位置,交换位置后 [50, 45, 55, 60, 67];
继续比较 55 和 60 ,由于 55 比 60 小,两者不需要交换位置;
(4)第 4 轮排序:从数组的头部开始向尾部依次排序。
首先比较 50 和 45 ,由于 55 比 45 大,两者需要交换位置,交换位置后 [45, 50, 55, 60, 67];
继续比较 50 和 55 ,由于 50 比 55 小,两者不需要交换位置;
升序排序:1
2
3
4
5
6
7
8
9
10
11
12
13
14public static int[] bubbleAscSort(int[] arr) {
int size = arr.length;
int temp;
for (int i = 1; i < size; i++) {
for (int j = 0; j < size - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
降序排序:只需要更换一下比较运算符即可1
2
3
4
5
6
7
8
9
10
11
12
13
14public static int[] bubbleDescSort(int[] arr) {
int size = arr.length;
int temp;
for (int i = 1; i < size; i++) {
for (int j = 0; j < size - i; j++) {
if (arr[j] < arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
冒泡排序的优化
常规冒泡排序采用双层循环的方式,单向循环,通过比较相邻数据,进行交换实现排序,优化方向就两点:冒泡排序优化之减少无用循环轮数
假设初始数据是:[67, 45, 50, 55, 60],那么实际上有效的排序只需要一轮,数据就已经排序完毕。但是默认还是会进行 4 轮排序。
可以设置一个标志,标记本轮排序是否存在数据交换,若没有交换则不需要进行下一轮排序,反之则需要继续下一轮排序
以升序排序为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static int[] bubbleAscSort(int[] arr) {
boolean isAscSorted = false;
int size = arr.length;
int temp;
for (int i = 1; !isAscSorted && i < size ; i++) {
isAscSorted = true;
for (int j = 0; j < size - i; j++) {
if (arr[j] > arr[j + 1]) {
isAscSorted = false;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
那么,在第 2 轮排序时,因为没有数据交换,则标志被标记为已排序,第 3 轮排序 和 第 4 轮排序则不必进行,这样可以减少排序轮数。冒泡排序优化之减少无用轮内循环比较
假设初始数据是:[45, 55, 67, 50, 60]
以带标记的升序排序为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public static int[] bubbleAscSort(int[] arr) {
boolean isAscSorted = false;
int size = arr.length;
int maxSwapIndex = size - 1;
int temp;
int lastSwapIndex = 0;
for (int i = 1; !isAscSorted && i < size ; i++) {
isAscSorted = true;
for (int j = 0; j < maxSwapIndex; j++) {
if (arr[j] > arr[j + 1]) {
isAscSorted = false;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
lastSwapIndex = j;
}
}
maxSwapIndex = lastSwapIndex;
}
return arr;
}
一轮排序:[45, 55, 50, 60, 67]
二轮排序:[45, 50, 55, 60, 67]
三轮排序:[45, 50, 55, 60, 67]
排序结果:[45, 50, 55, 60, 67]
开始排序:
标记单轮最大的数据交换位置 maxSwapIndex :size - 1
标记最后一次数据交换位置 lastSwapIndex : 0
(1)第 1 轮排序:从数组的头部开始向尾部依次排序:[45, 55, 67, 50, 60]
首先比较 45 和 55 ,由于 45 比 55 小,两者不需要交换位置,数据:[45, 55, 67, 50, 60];
继续比较 55 和 67 ,由于 55 比 67 小,两者不需要交换位置,数据:[45, 55, 67, 50, 60];
继续比较 67 和 50 ,由于 67 比 50 大,两者需要交换位置,交换位置后 [45, 55, 50, 67, 60],以本次数据交换位置标记为最后一次数据交换位置,lastSwapIndex = 2;
继续比较 67 和 60 ,由于 67 比 60 小,两者需要交换位置,交换位置后 [45, 55, 50, 60, 67],以本次数据交换位置标记为最后一次数据交换位置,lastSwapIndex = 3;
标记下一轮最大的数据交换位置 maxSwapIndex = 3;即后续位置的元素顺序已经排序完毕。
(2)第 2 轮排序:从数组的头部开始向尾部依次排序:[45, 55, 50, 60, 67]
首先比较 45 和 55 ,由于 45 比 55 小,两者不需要交换位置;
继续比较 55 和 50 ,由于 55 比 50 大,两者需要交换位置,交换位置后 [45, 50, 55, 60, 67],以本次数据交换位置标记为最后一次数据交换位置,lastSwapIndex = 2;
继续比较 55 和 60 ,由于 55 比 60 小,两者不需要交换位置;
j 自增后已经是 3 ,不小于 maxSwapIndex ,结束本轮排序
标记下一轮最大的数据交换位置 maxSwapIndex = 2;即后续位置的元素顺序已经排序完毕。
(3)第 3 轮排序:从数组的头部开始向尾部依次排序,数据:[45, 50, 55, 60, 67]
首先比较 45 和 50 ,由于 45 比 50 小,两者不需要交换位置;
继续比较 50 和 55 ,由于 50 比 55 小,两者不需要交换位置;
j 自增后已经是 2 ,不小于 maxSwapIndex ,结束本轮排序
且 isAscSorted 被标记为 已排序,结束排序冒泡排序优化之双向冒泡
以升序排序为例:
双向冒泡即结合上述升序排序和降序排序,在一轮排序内先正向升序排序一次,然后从最后交换数据的位置往前再进行一次反向的降序排序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public static int[] bubbleAscSort(int[] arr) {
int size = arr.length;
int maxSwapIndex = size - 1;
int minSwapIndex = 0;
int temp;
int lastSwapIndex = 0;
while (maxSwapIndex > minSwapIndex) {
for (int i = minSwapIndex; i < maxSwapIndex; i++) {
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
lastSwapIndex = i;
}
}
maxSwapIndex = lastSwapIndex;
for (int j = maxSwapIndex; j > minSwapIndex; j--) {
if (arr[j] < arr[j - 1]) {
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
lastSwapIndex = j;
}
}
minSwapIndex = lastSwapIndex;
}
return arr;
}