冒泡排序
1. 算法概述
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
2. 算法原理
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个(将大的那一个元素往后移动,小的元素往前移动)。
- 从开始第一对,到最后一对,依次比对每一次相邻元素,重复
1
的操作,这样执行完成后,最后的元素应该是最大的数。 - 重复上述步骤,除了前几次已经排序好的大数。即前几次每次排序的最大数
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
3. 动图演示
4. 代码实现
@Test
public void dubbleSort() {
int[] arr = new int[]{18, 34, 33, 30, 20};
log.info("sort before:{}", JSON.toJSON(arr));
//外循环,为什么length-1?因为:内循环会遍历每一个元素,进行两两比较,内循环每循环结束一次,会有一个最大值排在末尾索引
//所以外循环可以-1,减少一次空循环
for (int i = 0; i < arr.length - 1; i++) {
//内循环,为什么length-i-1,因为:内循环每循环结束一次(即外循环次数:i),就会产生一个最大值排在末尾索引
//所以,内循环不再判断本次之前选举的最大值,这就是length-i
//又因为:内循环会判断本次循环索引j和之后一个索引位置j+1的元素进行对比,外循环第一次轮训,内循环会全部执行
//则当内循环在对比最后一位元素时,会出现索引越界异常ArrayIndexOutOfBoundsException
//所以内循环判断条件为length-i-1,同时也防止上一次最大值重复比对。
for (int j = 0; j < arr.length - i - 1; j++) {
//比较当前索引是否>后一位索引,如果满足条件则,交换两者位置
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
log.info("sort after:{}", JSON.toJSON(arr));
}
5. 算法优化
5.1 减少非必要的比较
在某些时候,循环还未终止,整个数组已经排好序,此时应及时终止循环。(冒泡每次都会比较相邻两个数并交换次序不对的组,若一次循环后,都没进行交换,则已经完成排序)。
代码实现如下:
@Test
public void bubbleSort1() {
int[] arr = new int[]{18, 20, 30, 34, 33};
log.info("sort before:{}", JSON.toJSON(arr));
for (int i = 0; i < arr.length - 1; i++) {
//内循环执行完后,未发生交换,则证明顺序已经排序完成
boolean bool = true;
for (int j = 0; j < arr.length - i - 1; j++) {
//比较当前索引是否>后一位索引,如果满足条件则,交换两者位置
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
bool = false;
}
}
//如未发生交换,则退出循环
if (bool) {
break;
}
}
log.info("sort after:{}", JSON.toJSON(arr));
}
5.2 鸡尾酒排序(双向冒泡排序)
鸡尾酒是冒泡排序的升级版,该排序从左往右找出最大值后。再从右往左,找出最小值,类似鸡尾酒搅拌左右循环。在某些情况下,优于冒泡排序。
以序列(2,3,4,5,1)为例,鸡尾酒排序只需要访问两次(升序降序各一次 )次序列就可以完成排序,但如果使用冒泡排序则需要四次。
初始数组:{8,2,3,1,9}
第一次排序:
从头到尾,将大数排在数组尾部
第 1 次比较:8>2 交换位置:{8,2,3,1,9} -> {2,8,3,1,9}
第 2 次比较:8>3 交换位置:{2,8,3,1,9} -> {2,3,8,1,9}
第 3 次比较:8>1 交换位置:{2,3,8,1,9} -> {2,3,1,8,9}
第 4 次比较:8<9 不做处理:{2,3,1,8,9}
从尾到头,因9的排序位置已经确认,所以忽略9,从8开始,将小数排在数组头部
第 1 次比较:8>1 不做处理:{2,3,1,8,9}
第 2 次比较:1<3 交换位置:{2,3,1,8,9} -> {2,1,3,8,9}
第 3 次比较:1<2 交换位置:{2,1,3,8,9} -> {1,2,3,8,9}
此时:最小的数值已经排序出来
第二次排序:
从头到尾,将大数排在数组尾部,因1的排序位置已经确认,所以忽略1,从2开始,将大数排在数组尾部
第 1 次比较:2<3 不做处理:{1,2,3,8,9}
第 2 次比较:3<8 不做处理:{1,2,3,8,9}
至此,排序完成
代码实现如下:
public void bubbleSort3() {
int[] arr = new int[]{2, 3, 4, 5, 1};
log.info("sort before:{}", JSON.toJSON(arr));
//从头到尾,再从尾到头,为一次完整回合,所以,做多遍历 arr.length / 2 次
for (int i = 0; i < arr.length >>> 1; i++) {
boolean bool = true;
//每一回合,都会选举出来两个值,一个是相对最小,一个相对最大。为了进行浪费操作,所以要减去外层循环的次数 -i
for (int l2h = i; l2h < arr.length - i - 1; l2h++) {
if (arr[l2h] > arr[l2h + 1]) {
swap(arr, l2h, l2h + 1);
bool = false;
}
}
if (bool) {
break;
} else {
bool = true;
}
//arr.length - 2 - i = arr.length - 1 - (i + 1)
//同上,arr.length-1-i 为上面循环选定的最大值,需要向前推一位 即 -(i+1)
for (int h2l = arr.length - 2 - i; h2l > i; h2l--) {
swap(arr, h2l, h2l - 1);
bool = false;
}
if (bool) {
break;
}
}
log.info("sort after:{}", JSON.toJSON(arr));
}