持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
由于在学校的数据结构实验上没有想出来冒泡排序的优化版本,痛定思痛,决定了解一下冒泡排序的原理以及方法,并且用写博客的方式来巩固一下自己了解到的东西,权当作是一个自己的知识输出过程,希望有不足的地方可以提出来,我会认真看每个评论提出来的问题并进行一定的优化的,谢谢大家~
什么是冒泡排序
冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,是因为越小的元素会通过数据之间不断的两两交换慢慢浮到数组的顶端,如果碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样。
冒泡排序的原理
冒泡排序的每一趟都只能将一个最大或者最小的元素归位(取决于升序还是降序) ,也可以叫做是把最大的元素沉底或者把最小的元素浮出。以此类推,如果有n个数进行排序,只需要将n-1个数归位,也就是进行n-1趟排序
而每一趟都需要从第一位元素开始,与其相邻的元素进行比较然后决定是否交换位置,交换位置后(此时索引指向下一位元素)继续比较接下来的元素与其下一位元素的大小关系并且决定是否交换位置,重复此步骤,直到所有元素到达其正确的位置
第一种姿势:原始版的冒泡排序(升序)
每一趟排序完成之后,都代表有一个最大元素(无序区间最大) 归位,比如:第一趟就会有第一大的元素沉底,该元素排序完成后的索引与第一趟的i = length对应(即该最大元素已经沉到数组的最后一个位置)。第二趟就会有第二大的元素沉底,索引是i = length-1。随着趟数的增加, 有序区域的开始索引就会从i = length 开始不断减小,有序区域不断增大,因此我们在进行元素的两两比较的时候就不需要再比较有序区域的部分。
因此内部的循环就可以以j<i作为循环条件,一旦j>i,说明此时j已经进入了有序区域的范围,不需要再进行比较和交换元素。
下面是第一种冒泡排序姿势的实现
void bubbleSort(int[] arr){
//arr的第一个元素闲置,不放元素
for(int i = arr.length;i>0;i--){
for(int j = 1;j<i;j++){
if(arr[j]>arr[j+1]){
swap(arrp[j],arr[j+1]);//交换两个元素的位置
}
}
}
}
问题:当最外层执行到最后几轮的时候,若整个数组已经是处于排序完成的状态,但根据以上代码,仍会执行完最后几轮,造成处理时间的增加,如何避免?
第二种姿势:使用布尔变量change作为数组是否有序的标志
使用布尔变量change作为标志,当某一趟中没有发生元素的交换的时候,说明此时数组已经是有序状态了,不再需要进行排序,直接跳出最外层循环
下面是第二种冒泡排序姿势的实现
void bubbleSort(int[] arr){
//arr的第一个元素闲置,不放元素
for(int i = arr.length;i>0;i--){
//默认数列是处于有序的状态
boolean change = true;
for(int j = 1;j<i;j++){
if(arr[j]>arr[j+1]){
//进行元素交换
swap(arr[j],arr[j+1]);
//由于进行了元素交换,此时数组并不一定处于有序的状态
change = false;
}
}
if(change==true){
//数组已经是有序状态,跳出最外层循环
break;
}
}
}
问题:最外层的循环多余问题已经解决,对于内部的循环也同样存在循环多余的问题,当内部循环运行到中间的时候,后面的数组已经处于有序的状态,此时已经不需要再执行接下来的代码,但是根据以上代码,仍会把这一次循环执行完毕,并且在后面的内部循环也会执行完毕,该如何解决?
第三种姿势:将布尔变量change转换成一个int变量,用来记录数组有序区间的开始索引
经过前两种方式,我们可以发现,数组的有序区间的开始索引其实跟我们的最外层循环 i 有关系:i 就是数组种有序取域开始的索引,那我们只需要将布尔变量change转换成一个int变量,用来记录从哪里开始,数组接下来的部分都是有序的。
而这个change作为内层循环结束的条件,就可以避免内层循环继续到有序区域中进行无效的排序。
思路:我们先初始化change为0,每次进行内层循环的交换元素时用change记录下有序区域的起始索引,然后将change的值作为下一次内部循环的结束索引,就可以防止内层循环中在有序区域内进行无效排序的问题,
注意:这个int类型的change变量同时也起到了姿势二中防止外层循环多余执行的问题。当change=0时,就代表当前的数组已经没有进行任何的元素交换,处于有序的状态,所以我们可以通过判断change是否=0来跳出最外层循环
void bubbleSort(int[] arr){
//arr的第一个元素闲置,不放元素
int change = 0;//change初始化
for(int i = arr.length;i>0;i--){
//首先判断这个数组是否已经是有序的
if(change>0){
i = change;//更新有序区域的起始索引 i
change = 0;//重新设置为0,用于判断下一次的循环中数组是否处于有序状态
}
for(int j = 1;j<i;j++){
if(arr[j]>arr[j+1]){
swap(arrp[j),arrp[j+1];//交换元素
change = j;//记录下有序区域的起始位置
}
}
if(change==0){
//在最近的一次内层循环中已经没有进行过任何的元素交换,数组此时是有序状态,跳出循环,完成排序
break;
}
}
}
\