目录
1. 基本思想
- 每次比较两个相邻的元素,如果它们的顺序错误就将它们交换过来。
2. 排序原理
- 将数列中所有相邻的两个元素进行比较。每一次对数列进行遍历都可以找到现有数字中最大的数,当本次遍历完成后,该数将被冒泡到未排好序数中的最后一位,最终在多次遍历后完成排序。
3. 排序过程
- 以从小到大排列为例,设原数组 [89, 23, 45, 7],灰色即当次比较并处理好的区域
- 第一趟
- 第一次 [23, 89, 45, 7]
- 第二次 [23, 45, 89, 7]
- 第三次 [23, 45, 7, 89]
- 第二趟
- 第一次 [23, 45, 7, 89]
- 第二次 [23, 7, 45, 89]
- 第三趟
- 第一次 [7, 23, 45, 89]
4.复杂度分析
平均时间复杂度 | O(n^2) |
最好情况 | O(n) |
最坏情况 | O(n^2) |
空间复杂度 | O(1) |
稳定性 | 稳定 |
5.代码实现 (以Java为例)
// java
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
System.out.print("请输入数组的长度:");
int size = in.nextInt();
int[] a = new int[size];
System.out.println("请输入数组:");
for(int i = 0; i < size; i++){
a[i] = in.nextInt();
}
// 冒泡排序
for(int i = 0; i < a.length; i++ ){
for(int j = 0; j < a.length-i-1; j++){
if(a[j] > a[j+1]){ // 从小到大
int temp = a[j]; // 满足条件,交换
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
System.out.println("排序后数组:");
// 输出数组
for(int k = 0; k < a.length; k++){
System.out.print(a[k] + " ");
}
}
}
6.运行结果
7.两种优化方案
- 方案一
设置一个值,判断当轮冒泡有无发生交换,若没有交换说明数组已经有序,可直接退出循环
// 冒泡排序
for(int i = 0; i < a.length; i++ ){
boolean swap = false; // 设置布尔值判断有无交换
for(int j = 0; j < a.length-i-1; j++){
if(a[j] > a[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
swap = true; // 有交换改变布尔值
}
}
if(!swap) { // 无交换,说明数组有序,直接退出循环
break;
}
}
- 方案二
在方案一的基础上,记录最后一次发生交换的位置,因为在最后一次交换的时候,那个位置之后的元素已经有序,后续冒泡时就无需比较该位置后的元素。
// 冒泡排序
int lastIndex = 0;
int lastBorder = a.length - 1;
for(int i = 0; i < a.length; i++ ){
boolean swap = false;
if(i != 0){ // 防止初次遍历数组越界
lastBorder = lastIndex; // 后边界,说明之后的数字有序,无需再比较
}
for(int j = 0; j < lastBorder; j++){
if(a[j] > a[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
swap = true;
lastIndex = j; // 记录最后一次交换位置,作为后边界
}
}
if(!swap) {
break;
}
}
8.各排序算法效率
- 在数据随机的情况下,可以参考下面文章中的图表
- 算法性能:十种常见排序算法时间效率直观对比 - 活的潇洒80 - 博客园