一、什么是冒泡排序?
冒泡排序(Bubble sort),是一种较简单的排序算法。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。(PS:来源百度百科)
二、算法原理(升序)
- 1、比较相邻的两个元素。若第一个比第二个大,交换位置,反之位置不变。
- 2、交换位置后,再与后面的一个两两比较。
- 3、针对所有元素重复以上动作,最后一个除外。
举例来说:36,26,27,2,4,19,50,48 来说,会有多趟排序(size - 1),每一趟要排多次(size - k),这里列举出第一趟:
多趟排序的后的结果总结是这样的:
PS:这里留下一个思考,图里留了一个注意字样,在这里数组已经排序好了,但是还是会依次比较,你能想到什么方法优化吗?
它的动态执行过程如下动图所示:
PS:此动图来源于网络。
最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 是否稳定 |
---|---|---|---|
O(n^2) | O(n) | O(1) | 稳定 |
三、代码实现
public class BubbleSort {
public static void main(String[] args) {
// 直接定义一个数组
int[] nums = {36,26,27,2,4,19,50,48};
System.out.println("排序前的数组:" + Arrays.toString(nums));
sort(nums);
System.out.println("排序前的数组:" + Arrays.toString(nums));
}
/*
* 对数组进行冒泡排序
*
* @param nums 需要排序的数组
* */
private static void sort(int[] nums) {
// 数组长度 len
int len = nums.length;
// 临时变量 temp
int temp = 0;
// 外层循环控制排序趟数,len个进行len - 1趟
for (int i = 0; i < len - 1; i++) {
System.out.println("第"+ (i + 1) +"趟:");
// 内层循环控制比较的次数,第i趟比较i-1次
for (int j = 0; j < len - 1 - i; j++) {
// 比较相邻的两个元素,若前面的数字大于后面的,交换位置
if (nums[j] > nums[j+1]) {
temp = nums[j+1];
nums[j+1] = nums[j];
nums[j] = temp;
}
System.out.println(" "+"第"+ (j + 1) +"次:" + Arrays.toString(nums));
}
}
}
}
在这里插一嘴,想起来自己刚接触冒泡排序的时候,感觉那个时候的自己就是一个菜逼,哈哈哈哈哈,不理解两个for的意义,写了好几遍之后还是不理解,似懂非懂。对了,当时还对交换数据位置哪里每次写都要纠结好久,想着怎么交换呀,拿笔在纸上比划半年,才写出来,后来找到了一个小技巧:起手临时变量,中间半交叉,收尾临时变量。仅供参考,大佬勿喷(小声BB)
输出的结果:
从运行的结果可以看出,从红色标记开始,后面的排序的已经是有序的,但是代码还是要往后面执行,因此说明代码是可以进行优化,这里你可以思考一下怎么去优化这个问题。
4、优化:
增加一个标志位,当每次发生交换的时候,就进行标记,如果没有发生交换,不标记,也就是说此时数组已经有序,直接跳出循环,于此同时再记录一下最后一次交换元素的位置,即可完成对这个排序的优化。
代码如下所示:
public class BubbleSort {
public static void main(String[] args) {
// 直接定义一个数组
int[] nums = {36,26,27,2,4,19,50,48};
System.out.println("排序前的数组:" + Arrays.toString(nums));
sort(nums);
System.out.println("排序前的数组:" + Arrays.toString(nums));
}
/*
* 对数组进行冒泡排序
*
* @param nums 需要排序的数组
* */
private static void sort(int[] nums) {
// 数组长度 len
int len = nums.length - 1;
// 记录最后一个交换的位置
int pos = 0;
// 临时变量 temp
int temp = 0;
// 外层循环控制排序趟数,len个进行len - 1趟
for (int i = 0; i < nums.length - 1; i++) {
System.out.println("第" + (i + 1) + "趟:");
// 标记位
boolean flag = true;
// 内层循环控制比较的次数,第i趟比较i-1次
for (int j = 0; j < len; j++) {
// 比较相邻的两个元素,若前面的数字大于后面的,交换位置
if (nums[j] > nums[j + 1]) {
temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
// 如果能走到这里说明有有元素交换,还不是有序的,标记为false
flag = false;
// 最后一次交换元素的位置
pos = j;
}
System.out.println(" 第" + (j + 1) + "次:" + Arrays.toString(nums));
}
// 如果没有元素交换,直接跳出循环
if (flag) {
break;
}
// 把最后一次交换元素的位置赋值给len,即下一次比较到记录位置
len = pos;
}
}
}
输出的结果:
对于优化这块,我觉得并不具备普遍性,如果是在最坏的时间复杂度的情况下,优化与不优化输出的结果都是一样的,所以我们只需要了解冒泡的思想即可。另外加标志位这个思想在实际的开发中也很常用,需要我们多多思考。
水平有限,写的不好的地方,希望大家指出。如果你感觉这篇文章对你有帮助,点赞支持一下