冒泡排序(Bubble sorting)
就像气泡一样,小气泡往上冒变成大气泡
- 从头开始比较每一对相邻的元素,如果左边的元素大于右边的元素,则调换位置(一轮扫描结束后,最大的数在最后面)
- 忽略1中找到的最大元素,重复1,直到元素有序
这里以升序为例,降序反之即可
图像演示:
从下标1开始
发现左边(3)比本身(2)大,所以要调换位置
然后下标+1继续比较
发现左边(3)比本身(4)小,不处理,下标继续+1比较,直到下标为4时,发现左边大于本身于是调换位置
此时下标要回到一开始(也就是下标为1),第二轮就不用处理最后那个最大的元素了(也就是下标为4的最大元素)
第二轮最后:
第三轮最后:
第四轮:
此时 退出循环
代码演示
public static void BubbleSorting1(int[] ints) {
for (int end = ints.length - 1; end > 0; end--) {//每扫描完一轮,最后一个元素都不用再扫描
for (int start = 1; start <= end; start++) {//下标从1开始扫描比较
if (ints[start - 1] > ints[start]) {//如果左边大于右边(本身)则交换位置
int temp = ints[start - 1];
ints[start - 1] = ints[start];
ints[start] = temp;
}
}
}
}
优化
优化一
如果序列有序了,我们就可以提前终止循环
什么时候序列有序呢?
如果我们一轮扫描没有发生位置的交换说明序列有序可以提前终止循环,我们可以使用打标记
public static void BubbleSorting2(int[] ints) {
for (int end = ints.length - 1; end > 0; end--) {
boolean flag = true;//代表是 有序
for (int start = 1; start <= end; start++) {
if (ints[start - 1] > ints[start]) {
int temp = ints[start - 1];
ints[start - 1] = ints[start];
ints[start] = temp;
//如果发生了位置交换 说明不是有序
flag = false;
}
}
if (flag) break;//如果是有序 直接退出循环
}
}
如果在大概率无序的情况下,使用这种方法效率会比第一种方法还低
优化二
如果序列尾部局部有序,可以记录下最后一次比较的位置,下一轮扫描就不用比较比这个位置还后的下标了,减少比较次数
public static void BubbleSorting3(int[] ints) {
for (int end = ints.length - 1; end > 0; end--) {
int lastSort = 1;
for (int start = 1; start <= end; start++) {
if (ints[start - 1] > ints[start]) {
int temp = ints[start - 1];
ints[start - 1] = ints[start];
ints[start] = temp;
lastSort = start;//记录这次位置交换的下标
}
}
end = lastSort;
}
}
还发现如果序列本来就有序,不发生位置交换,lastSort为1赋值给end 就正好满足了优化一的那种方法
总结
- 时间复杂度
- 最好:O(n) 【有序序列,外循环只执行1次时】
- 最坏,平均:O(n^2)
- 空间复杂度
- O(1)
- 稳定性
- 稳定(如果将左边大于右边就交换位置,改为左边大于等于右边交换位置则会变的不稳定)
- In-place
- 最好:O(n) 【有序序列,外循环只执行1次时】
- 最坏,平均:O(n^2)
- 空间复杂度
- O(1)
- 稳定性
- 稳定(如果将左边大于右边就交换位置,改为左边大于等于右边交换位置则会变的不稳定)
- In-place
- 因为冒泡排序没有依赖额外空间资源所以是原地算法
若有错,麻烦指出,感谢