一、什么是冒泡排序
冒泡排序(BubbleSort),顾名思义,由于整个排序的过程重复进行多次比较和交换元素位置的操作,就好像水中气泡上浮的状态,故得名。冒泡排序的整个过程分为多轮,每轮又分为多次(轮次数由需要排序的元素个数决定),以升序为例,每一轮冒泡都是从第1个元素开始,依次比较1&2,2&3,3&4,4&5……如果在比较的过程中发现前一个元素大于后一个元素,则将二者位置调换,因此可以实现每一轮冒泡,都能将该轮所有元素中的最大元素排到该轮所有元素末尾。这样经过多轮冒泡,就能保证所有元素的顺序排列。
二、冒泡排序的排序过程
以升序规则(最终实现从小到大排列)为例,我们看一下冒泡的具体实现过程:
可以看到第一轮排序经过5次比较+交换位置的操作,最终实现了将所有元素中最大的元素排到了倒数第一个位置。
由于第一轮已经将最大元素9找到并放在了最后的位置,所以第二轮9这个元素不再参加比较;第二轮排序经过4次比较+交换位置的操作,最终实现了将所有元素中第二大的元素排到了倒数第二个位置。
同上,本轮元素7和元素9不再参加比较; 第三轮排序经过3次比较+交换位置的操作,最终实现了将所有元素中第三大的元素排到了倒数第三个位置。
由于数组实例的特殊性其实我们发现第三轮排序过后,升序排列的需求已经实现了,但为了演示冒泡排序的全过程,这里先不省略后续步骤。
第四轮排序经过2次比较+交换位置的操作,最终实现了将所有元素中第四大的元素排到了倒数第四个位置。
第五轮排序经过1次比较+交换位置的操作,最终实现了将所有元素中第五大的元素排到了倒数第五个位置。
由于数组共有6个元素,我们已经按顺序定位好了其中5个,因此最小的元素位置也得以确定,冒泡排序到此结束。
三、代码实现
可以看到排序过程有多轮多次,因此这里我们使用双层循环来实现,具体如下:
public class BubbleSortDemo {
public static void main(String[] args) {
int[] nums = {6, 2, 9, 5, 3, 7};
System.out.println("排序前原始数组: " + Arrays.toString(nums));
bubbleSort(nums);
System.out.println("排序后的数组: " + Arrays.toString(nums));
}
private static void bubbleSort(int[] arr) {
/*
外层循环,对应轮数;以本例而言数组共6个元素,进行五轮冒泡之后
已经从大到小从后到前确定了5个元素的位置,最后一个元素也就不用在冒泡了,
必然在第一个位置,所以这里的循环终止条件为i < arr.length - 1
*/
for (int i = 0; i < arr.length - 1; i++) {
/*内层循环,对应每轮的次数,应逐轮递减(arr.length - 1- i)*/
for (int j = 0; j < arr.length - 1- i; j++) {
//比较交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j];//定义临时中间变量用于两个位置的值交换
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
运行结果如下:
至此,冒泡排序的整个流程我们已经了解,但是上述过程中仍然存在问题;比如每一轮冒泡的过程,可能不止移动了当前轮最大的元素,其中较大的元素也可能在向后移动位置,又比如由于原始数组的不确定性,冒泡排序不一定要经过(元素数-1)轮才能完成排序,提前完成排序又会导致后面的循环轮次在do nothing,因此我们来通过优化代码解决一下这个问题:
思路:冒泡过程中的某一轮如果没有进行任何位置交换,我们就认为排序已经完成,此时直接停止循环,程序结束。
优化后代码:
public class BubbleSortDemo {
public static void main(String[] args) {
int[] nums = {6, 2, 9, 5, 3, 7};
System.out.println("排序前原始数组: " + Arrays.toString(nums));
bubbleSort(nums);
System.out.println("排序后的数组: " + Arrays.toString(nums));
}
private static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {//外层循环,对应轮数
boolean b = false;//每一轮冒泡开始前定义boolean变量
for (int j = 0; j < arr.length - 1 - i; j++) {//内层循环,对应每轮的次数
//比较交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
b = true;//进行了位置交换,所以让b为true
}
}
if (!b) {//b仍为false表示该轮没有进行位置交换,即所有元素已经有序
break;//退出循环
}
}
}
}
经过优化的代码会比前面的代码少进行若干轮冒泡循环,我们可以设置count属性记录冒泡轮数来做比较,这里不再赘述。
四、ending
冒泡排序是所有排序算法的典中典,初学者大都会经历由懵到懂再到“切,什么玩意”的过程,另外冒泡排序也有其他写法,比如双层循环的逻辑等等。
2023年3月29日00:39:56 dgz