一、什么是冒泡排序
冒泡排序的思想: 比较相邻两个数,把大的数放在右边(也可以把小的数放在右边,看排序规则,这里按从小到大排序进行说明)。所以,当第一趟排序结束后(比较了n - 1次,n为数组元素个数),最大的数就到了最后面,其余的数还是处于无序状态;接着进行第二趟排序(比较了n - 2次),把第二大的数放到倒数第二位;依次类推:每一趟排序比较的次数减1,一共比较n - 1趟,排序结束。如下图所示:
举例说明,要排序数组为:{6,3,8,2,9,1},过程如下(按从小到大排列):
第一趟排序:
第一次排序:6和3比较,6大于3,交换位置: 3 6 8 2 9 1
第二次排序:6和8比较,6小于8,不交换位置: 3 6 8 2 9 1
第三次排序:8和2比较,8大于2,交换位置: 3 6 2 8 9 1
第四次排序:8和9比较,8小于9,不交换位置:3 6 2 8 9 1
第五次排序:9和1比较:9大于1,交换位置: 3 6 2 8 1 9
第一趟总共进行了5次比较, 排序结果: 3 6 2 8 1 9
---------------------------------------------------------------------------------------------------------------
第二趟排序:
第一次排序:3和6比较,3小于6,不交换位置:3 6 2 8 1 9
第二次排序:6和2比较,6大于2,交换位置: 3 2 6 8 1 9
第三次排序:6和8比较,6大于8,不交换位置:3 2 6 8 1 9
第四次排序:8和1比较,8大于1,交换位置: 3 2 6 1 8 9
第二趟总共进行了4次比较, 排序结果: 3 2 6 1 8 9
---------------------------------------------------------------------------------------------------------------
第三趟排序:
第一次排序:3和2比较,3大于2,交换位置: 2 3 6 1 8 9
第二次排序:3和6比较,3小于6,不交换位置:2 3 6 1 8 9
第三次排序:6和1比较,6大于1,交换位置: 2 3 1 6 8 9
第三趟总共进行了3次比较, 排序结果: 2 3 1 6 8 9
---------------------------------------------------------------------------------------------------------------
第四趟排序:
第一次排序:2和3比较,2小于3,不交换位置: 2 3 1 6 8 9
第二次排序:3和1比较,3大于1,交换位置: 2 1 3 6 8 9
第四趟总共进行了2次比较, 排序结果: 2 1 3 6 8 9
---------------------------------------------------------------------------------------------------------------
第五趟排序:
第一次排序:2和1比较,2大于1,交换位置: 1 2 3 6 8 9
第五趟总共进行了1次比较, 排序结果: 1 2 3 6 8 9
---------------------------------------------------------------------------------------------------------------
最终结果:1 2 3 6 8 9
由此可见,冒泡排序可以用双重循环实现,外层循环控制排序趟数,内层循环控制比较次数。
二、代码实现
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class BubbleSort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int len = sc.nextInt();
List<Integer> array = new ArrayList<Integer>();
for(int i = 0;i < len;i++) {
int element = sc.nextInt();
array.add(element);
}
bubbleSort(array);
print(array);
sc.close();
}
/**
*
* @param array 待排序序列
*/
public static void bubbleSort(List<Integer> array) {
for(int i = 1;i < array.size();i++) {
for(int j = 0;j < array.size() - i;j++) {
if(array.get(j) > array.get(j + 1)) {
int temp = array.get(j);
array.set(j, array.get(j + 1));
array.set(j + 1, temp);
}
}
}
}
public static void print(List<Integer> array) {
for(int item : array) {
System.out.print(item + " ");
}
}
}
三、冒泡排序的时间复杂度分析
显然,不管待排序的序列是否有序,上述的冒泡排序代码的时间复杂度都是O(n²),而且排序的算法是稳定的。所以我们可以对冒泡排序的算法进行优化,使得序列有序时,退出循环。
四、冒泡排序原始算法优化
优化思想:
当我们检测到还没有排序的序列有一部分是有序的时候,我们不对有序的那部分序列进行遍历,这样能大大减少比较次数。即:可以设置一个指针指向每次发生数据交换的位置,每一趟排序的最大边界就为这个位置(即这个位置以后的数据都是有序的,不再进行判断),这样会大大减少比较的次数。
举例说明,要排序数组为:{6,1,3,2,4,5},指针 index = len - 1 = 5(循环之外初始化的值),过程如下(按从小到大排列):
第一趟排序:
第一次排序:6和1比较,6大于1,交换位置: 1 6 3 2 4 5 index = 0
第二次排序:6和3比较,6大于3,交换位置: 1 3 6 2 4 5 index = 1
第三次排序:6和2比较,6大于2,交换位置: 1 3 2 6 4 5 index = 2
第四次排序:6和4比较,6大于4,交换位置: 1 3 2 4 6 5 index = 3
第五次排序:6和5比较:6大于5,交换位置: 1 3 2 4 5 6 index = 4
第一趟总共进行了5次比较, 排序结果: 1 3 2 4 5 6
---------------------------------------------------------------------------------------------------------------
每一趟排序开始的时候指针会初始化为0
第二趟排序:(内存循环的边界为4)
第一次排序:1和3比较,1小于3,不交换位置: 1 3 2 4 5 6 index = 0
第二次排序:3和2比较,3大于2,交换位置: 1 2 3 4 5 6 index = 1
第三次排序:3和4比较,3小于4,不交换位置: 1 2 3 4 5 6 index = 1
第四次排序:4和5比较,4小于5,不交换位置: 1 2 3 4 5 6 index = 1
第一趟总共进行了4次比较, 排序结果: 1 2 3 4 5 6
---------------------------------------------------------------------------------------------------------------
第三趟排序:(指针初始化为0,内存循环的边界为1,所以只进行一次比较)
第一次排序:1和2比较,1小于2,不交换位置: 1 2 3 4 5 6 index = 0
第一趟总共进行了1次比较, 排序结果: 1 2 3 4 5 6
---------------------------------------------------------------------------------------------------------------
从第三趟排序后可知:内层循环边界为0,表示序列全部有序,排序结束
五、优化后的代码
public static void bubbleSort_Optimizer(List<Integer> array) {
int index = array.size();
int len = index - 1;
while(index > 0) {
index = 0;
for(int i = 0; i < len;i++) {
if(array.get(i) > array.get(i + 1)) {
int temp = array.get(i);
array.set(i, array.get(i + 1));
array.set(i + 1, temp);
index = i;
}
}
len = index;
}
}
六、优化后的算法时间复杂度分析
从优化后的代码可知:当待排序序列为一个有序序列时,整个排序算法只执行了一次外层while循环和一次内层for循环,比较了n - 1,然后就会退出双重循环,排序结束,这是最好的情况,时间复杂度为O(n);反之,最坏的情况为O(n²),并且改进后的冒泡排序算法仍然是稳定的。