一、算法基础
1.1 什么是冒泡排序
冒泡排序是一种简单直观的比较排序算法。它重复地走访待排序的数列,依次比较相邻两个元素,如果顺序错误就交换它们,直到没有元素需要交换为止。
1.2 基本思想
- 比较相邻元素:从头开始,两两比较相邻元素
- 交换位置:如果前一个元素大于后一个元素,则交换位置
- 重复操作:每完成一次迭代,最大的元素会"冒泡"到数组末尾
- 多次迭代:重复以上步骤,每次排除已排好序的末尾元素
1.3 时间复杂度
- 最好情况:O(n),当数组已经排好序
- 最坏情况:O(n²),当数组逆序排列
- 平均情况:O(n²)
二、冒泡排序的分类
2.1 标准冒泡排序
标准版本每次将最大元素冒泡到末尾:
- 外层循环:控制排序轮数,最多n-1轮
- 内层循环:控制每轮比较次数,逐渐减少
- 无优化:即使数组已排序仍会执行全部循环
2.2 优化冒泡排序
通过标记变量检测一轮比较中是否有交换发生:
- 设置标记:初始假设本轮无交换
- 发生交换:更新标记变量
- 提前终止:若一轮比较无交换发生,则排序完成
三、冒泡排序实现
3.1 标准实现
java
public class BubbleSort {
/**
* 标准冒泡排序算法
* @param arr 待排序数组
*/
public static void bubbleSort(int[] arr) {
int n = arr.length;
// 外层循环控制排序轮数
for (int i = 0; i < n - 1; i++) {
// 内层循环进行相邻元素比较和交换
// 每轮结束后,最大的i+1个元素已经排好序
for (int j = 0; j < n - i - 1; j++) {
// 如果当前元素大于下一个元素,交换它们
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
3.2 优化实现
java
public class OptimizedBubbleSort {
/**
* 优化版冒泡排序算法
* @param arr 待排序数组
*/
public static void bubbleSort(int[] arr) {
int n = arr.length;
boolean swapped;
// 外层循环控制排序轮数
for (int i = 0; i < n - 1; i++) {
swapped = false;
// 内层循环进行相邻元素比较和交换
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
// 标记发生了交换
swapped = true;
}
}
// 如果没有发生交换,说明数组已经有序,提前结束
if (!swapped) {
break;
}
}
}
}
3.3 双向冒泡排序(鸡尾酒排序)
java
public class CocktailSort {
/**
* 双向冒泡排序(鸡尾酒排序)算法
* @param arr 待排序数组
*/
public static void cocktailSort(int[] arr) {
int left = 0;
int right = arr.length - 1;
boolean swapped;
while (left < right) {
swapped = false;
// 从左向右,将最大元素冒泡到右侧
for (int i = left; i < right; i++) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
swapped = true;
}
}
// 如果没有交换,数组已经有序
if (!swapped) {
break;
}
right--; // 最大元素已经到位,缩小右边界
swapped = false;
// 从右向左,将最小元素冒泡到左侧
for (int i = right; i > left; i--) {
if (arr[i] < arr[i - 1]) {
int temp = arr[i];
arr[i] = arr[i - 1];
arr[i - 1] = temp;
swapped = true;
}
}
// 如果没有交换,数组已经有序
if (!swapped) {
break;
}
left++; // 最小元素已经到位,缩小左边界
}
}
}
四、算法分析与优化
4.1 理论推导
冒泡排序的工作原理可以用以下数学表达式描述:
- 比较次数:(n-1) + (n-2) + ... + 1 = n(n-1)/2
- 最大交换次数:同上 n(n-1)/2
- 元素移动次数:最多3×n(n-1)/2(每次交换需要3次移动)
4.2 优化策略
- 提前终止优化:如前述,检测是否有交换发生
- 记录最后交换位置:每轮记录最后一次交换的位置,下一轮只需扫描到该位置即可
- 奇偶交替扫描:类似鸡尾酒排序,减少"乌龟"(小元素靠后)的情况
java
public class EnhancedBubbleSort {
/**
* 记录最后交换位置的优化冒泡排序
* @param arr 待排序数组
*/
public static void bubbleSort(int[] arr) {
int n = arr.length;
int lastSwappedIndex = n - 1;
int newLastSwappedIndex;
while (lastSwappedIndex > 0) {
newLastSwappedIndex = 0;
for (int i = 0; i < lastSwappedIndex; i++) {
if (arr[i] > arr[i + 1]) {
// 交换元素
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
// 记录最后交换的位置
newLastSwappedIndex = i;
}
}
// 更新下一轮排序的终止位置
lastSwappedIndex = newLastSwappedIndex;
}
}
}
五、完整示例程序
java
public class BubbleSortDemo {
public static void main(String[] args) {
// 测试数据
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组: ");
printArray(arr);
// 执行优化版冒泡排序
optimizedBubbleSort(arr);
System.out.println("\n排序后数组: ");
printArray(arr);
}
/**
* 优化的冒泡排序实现
*/
public static void optimizedBubbleSort(int[] arr) {
int n = arr.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - i - 1; j++) {
// 如果当前元素大于下一个元素,交换它们
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
swapped = true;
}
}
// 如果没有发生交换,数组已经有序
if (!swapped) {
System.out.println("提前结束于第 " + (i + 1) + " 轮");
break;
}
}
}
/**
* 交换数组中两个元素
*/
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 打印数组
*/
private static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
六、总结
冒泡排序是一种经典的排序算法,其特点如下:
6.1 优点
- 实现简单:代码直观易懂,适合教学使用
- 稳定性好:相等元素不会改变相对顺序
- 原地排序:不需要额外空间
- 自适应性:对于部分有序数据效率较高
6.2 缺点
- 效率低下:平均时间复杂度为O(n²)
- 比较次数多:即使数据已经有序,基本版本仍需大量比较
6.3 适用场景
- 小规模数据:元素数量较少时表现尚可
- 教学演示:算法思想简单直观
- 接近有序数据:优化版本在这种情况下效率较高
冒泡排序虽然在大规模数据中效率不高,但其思想简单,实现容易,是学习排序算法的良好起点。在实际应用中,可根据数据特性选择更高效的排序算法,如快速排序、归并排序等。