理论
冒泡排序也是入门学的最简单的排序算法之一,顾名思义,这个排序算法的思想呢就是每次都把最大的泡泡(数字)浮到最顶端去,然后再从后面把第二大的数向上浮,直到只剩最后一个泡泡了
实际的操作过程:
是将指针不断向后移动,并且对比相邻的两个数字,如果前面的大就交换位置,如果后面的大就不交换位置,不管是不是交换位置,之后都要将指针继续向上移动,然后再次对比
比如有如下数组
3,1,5,4,2
第一次循环
指针在第0个位置:1,3,5,4,2 因为3>1,交换
指针在第1个位置:1,3,5,4,2 因为3<5,不交换
指针在第2个位置:1,3,4,5,2 因为5>4,交换
指针在第3个位置:1,3,4,2,5 因为5>2,交换
至此第一次循环完成,再进行下一轮循环,因为最大的数已经冒到最上面了,所以下一次循环只需要指针只需要到第2个位置就可以了
每次循环都会将剩下的待排数组里面最大的数字冒到最上面来,最后会变成有序数组
性质
中文名称 | 英文名称 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|---|
冒泡排序 | Bubble | n2 | n2 | n | 1 | 稳定 |
最简单实现
public static void sort(int arr[]){
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
优化
但是这种写法呢,无论如何时间复杂度都是n2,所以还要加一点优化进来,让其最好时间复杂度能达到O(n)
1、首先加个标记,假设待排数组本来就是有序的,那么一轮冒泡下来,我们就可以确定不再需要下一轮冒泡了,所以可以加上标记来记录是否有交换发生
2、假设数组后半部分的数都在正确的位置,那么,每次循环我们就不再需要从后半部分来循环了,所以我们可以加一个标记,来记录我们发生替换的最高位置,下次循环不再遍历记录的最高位后面的部分
3、假设数组中间部分的元素都在正确的位置,志愿后最大值和最小值反了,按照原本的逻辑,最大值拍到最上面去了,那最小值移动到最下面的话还要循环n-2次,那么我们可不可以直接反向冒泡将最小值到最低端呢,当饭是可以的,就在一次循环除了正向冒泡后再加一次反向冒泡,那么相当于两冒泡便可以将数组排好序
其实用了第一种方式后最好的时间复杂度已经是O(n)了,但是我们不能止步于此,于是有了2和3
上代码
public static void sort2(int arr[]){
int high = arr.length - 1;//记录正向遍历的最大索引位置
int low = 1;//记录反向遍历的最小索引位置
for (int i = 1; i < arr.length; i++) {
boolean flag = true;
int pos = 0;
for (int j = 0; j < high; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = false;//记录是否发生过交换
pos = j;//记录当前比较到的最高位
}
}
if(flag){
return;
}
high = pos;
//反向遍历,记录反向的index最小值
flag = true;
for (int j = high; j > low; j--){
if(arr[j] < arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
flag = false;
i = j;
}
}
low = i;
if(flag){
return;
}
}
}