概述:
一种将数组中无序元素按照大小规则依次排列的方法。
最小或最大的元素会像水中的气泡一样"漂浮"到数组的一端,故得名"冒泡排序"。
原理:
对数组中的元素两两相比,根据比较的结果,将大小不同的元素通过第三变量互换、分开放置。当上述过程重复足够多次时,便实现对所有元素的有序排列。
我们也可以用一句话概括冒泡排序原理:相邻元素比较大小,交换位置,重复上述过程。
实现冒泡排序代码的流程结构:
按照我们之间学过的流程结构,我们可以确定冒泡排序需要用到的结构有:
顺序结构:用于实现交换位置的功能。
选择结构:用于实现判断相邻元素大小的功能。
循环结构:用于重复执行选择结构和顺序结构的功能。
冒泡排序的子功能
我们来按照功能依次展开说明:
1.交换位置功能:
我们先创建一个数组,在数组中放入元素。
int[] arr = {1,2,3,5,4};//数组的静态初始化
我们可以把数组看成一列火车,每个下标对应的位置都是一节车厢,一节车厢装着一个元素。
我们可以得到数组下标位置和元素的关系如下:
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
---|---|---|---|---|
1 | 2 | 3 | 5 | 4 |
我们现在要交换5和4元素的位置,使数组的顺序变成{1,2,3,4,5},怎么交换?
常见的方法是定义一个临时变量,用这个临时变量保存两个元素中的一个,相当于做了个备份,再将两个元素中的没有备份的赋值给另一个,把临时变量保存的元素、备份的赋值给那个一开始没有备份的元素,这样就完成了交换。
我们用图片来看更直观:
用代码实现就是这样:
int temp = arr[3];
arr[3] = arr[4];
arr[4] = temp;
可能有细心的小伙伴们好奇,如果我备份的是4不是5呢?
图片安排:
用代码实现就是这样:
int temp = arr[4];
arr[4] = arr[3];
arr[3] = temp;
看好了哈,临时变量的选取不影响互换结果。
2.判断相邻元素的大小功能:
我们使用if条件语句判断相邻元素大小关系是否满足条件。
在两相邻元素关系中,我习惯称下标位置小的元素为"左元素",下标位置大的元素为"右元素"。
根据冒泡排序原理,我们会先比较相邻元素大小,只有满足大小关系的两元素才交换位置。
如果"左元素"大于"右元素"
我们使用交换位置功能交换相邻位置的数据,"左右元素的大小关系"会反转,那么大的元素会在右边,小的元素会在左边。
"右元素"大于"左元素"同理。
3.重复执行和遍历元素的功能
不计交换位置的次数,
假如数组中只有2个元素,两两相较我们最少比较1次才实现整个数组的排序。
假如数组中有3个元素,两两相较,我们最少需要比较3次才实现整个数组的排序。
比较次数 | arr[0] | arr[1] | arr[2] |
---|---|---|---|
初始值 | 1 | 2 | 3 |
1次 | 2 | 1 | 3 |
2次 | 2 | 3 | 1 |
3次 | 3 | 2 | 1 |
假如数组中有4个元素,两两相较,我们最少需要比较6次才能实现整个数组的排序 | |||
比较次数 | arr[0] | arr[1] | arr[2] |
:-: | :-: | :-: | :-: |
初始值 | 6 | 7 | 8 |
1次 | 7 | 6 | 8 |
2次 | 7 | 8 | 6 |
3次 | 7 | 8 | 9 |
4次 | 8 | 7 | 9 |
5次 | 8 | 9 | 7 |
6次 | 9 | 8 | 7 |
通过观察数组下标位置和比较次数的变化,可以得出关系:
数组中有2个元素,比较并交换1次,即可完成排序。
数组中有3个元素,比较并交换2次,数组下标的循环变量需要回归到0,此时数组中有1个数处在最终位置,不需要交换。
剩下需要比较的元素,相当于只有2个元素数组的情况,数组下标的循环变量需要回归到0,只要比较并交换1次,整个数组即可完成排序,整个过程中数组下标的循环变量需要归零1次。
数组中有4个元素,比较并交换3次,数组下标的循环变量需要回归到0,此时数组中有1个数处在最终位置,不需要交换。
剩下需要比较的元素,相当于只有3个元素数组的情况,数组下标的循环变量需要回归到0,比较并交换2次,剩下需要比较的元素,相当于只有2个元素数组的情况,数组下标的循环变量需要回归到0,只要比较并交换1次,整个数组即可完成排序,整个过程中数组下标的循环变量需要归零3次。
也就是说,长度为x的数组,比较并交换(x-1)次,数组下标的循环变量需要回归到0,需要比较并交换(x-2)次,数组下标的循环变量需要回归到0,比较并交换(x-n)=1次,整个数组即可完成排序,整个过程中数组下标的循环变量需要归零(x-1)次,每次归零后比较并交换的次数要比上一次小1。
我们可以用嵌套for循环实现重复执行和遍历数组元素的功能,根据嵌套循环【1次外循环,1轮内循环】的特点:
外循环变量最小值【i = 0】、最大值【i = 数组长度 - 1】。由于【每次归零后比较并交换的次数要比上一次小1】且外循环变量有自增1的特点,因此内循环变量最小值【j = 0】、最大值【j = 数组长度 - 1 - i】。
所以我们可以写出代码:
int[] arr = {6,7,8,9};
System.out.print("初始顺序:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+",");
}
System.out.print("目标顺序:9,8,7,6");
System.out.println("\n-----------------");
int temp = 0;
int count = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
System.out.println("第"+(i + 1)+"轮第"+(j + 1)+"次");
//后小前大,倒序
if (arr[j + 1] > arr[j]){
System.out.print("互换前:");
for (int k :
arr) {
if (k == arr[j])
System.out.print("["+k+",");
else if(k == arr[j + 1])
System.out.print(k+"],");
else
System.out.print(k+",");
}
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
System.out.print("互换后:");
for (int k :
arr) {
if (k == arr[j])
System.out.print("["+k+",");
else if(k == arr[j + 1])
System.out.print(k+"],");
else
System.out.print(k+",");
}
System.out.println();
}else{
System.out.println("不满足条件,没有互换");
}
count++;
}
}
System.out.println("-----------------");
System.out.println("总共比较"+count+"次");
运行结果:
初始顺序:6,7,8,9,目标顺序:9,8,7,6
-----------------
第1轮第1次
互换前:[6,7],8,9,互换后:[7,6],8,9,
第1轮第2次
互换前:7,[6,8],9,互换后:7,[8,6],9,
第1轮第3次
互换前:7,8,[6,9],互换后:7,8,[9,6],
第2轮第1次
互换前:[7,8],9,6,互换后:[8,7],9,6,
第2轮第2次
互换前:8,[7,9],6,互换后:8,[9,7],6,
第3轮第1次
互换前:[8,9],7,6,互换后:[9,8],7,6,
-----------------
总共比较6次