冒泡排序原理详解
冒泡排序原理详解
基本思想
冒泡排序的核心思想是重复比较相邻元素,将较大的元素逐步"冒泡"到数组末尾。就像水中的气泡会自然上浮到水面一样,较大的元素会逐渐移动到数组的右端。
算法原理
1. 基本步骤
- 比较相邻元素:从数组第一个元素开始,比较相邻的两个元素
- 交换位置:如果前面的元素比后面的大(升序),就交换它们的位置
- 继续比较:继续向后比较下一对相邻元素
- 完成一轮:一轮结束后,最大的元素会"冒泡"到数组末尾
- 重复过程:重复上述过程,每轮都会确定一个位置
2. 为什么叫"冒泡"?
- 较大的元素像气泡一样逐渐上浮(向右移动)
- 每一轮都有一个最大元素"冒"到正确位置
- 整个过程就像连续的气泡上浮
详细执行过程
以数组 [10, 4, 2, 8, 6]
为例:
第1轮冒泡
初始: [10, 4, 2, 8, 6]
比较10和4: 10 > 4,交换 → [4, 10, 2, 8, 6]
比较10和2: 10 > 2,交换 → [4, 2, 10, 8, 6]
比较10和8: 10 > 8,交换 → [4, 2, 8, 10, 6]
比较10和6: 10 > 6,交换 → [4, 2, 8, 6, 10]
第1轮结束:[4, 2, 8, 6, 10] ← 最大值10已就位
第2轮冒泡
起始: [4, 2, 8, 6, 10]
比较4和2: 4 > 2,交换 → [2, 4, 8, 6, 10]
比较4和8: 4 < 8,不交换
比较8和6: 8 > 6,交换 → [2, 4, 6, 8, 10]
第2轮结束:[2, 4, 6, 8, 10] ← 次大值8已就位
第3轮冒泡
起始: [2, 4, 6, 8, 10]
比较2和4: 2 < 4,不交换
比较4和6: 4 < 6,不交换
第3轮结束:[2, 4, 6, 8, 10] ← 第三大值6已就位
后续轮次
由于数组已经有序,后续比较都不需要交换,算法结束。
标准冒泡排序代码
数组版本
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) { // 外层循环:控制轮数
for (int j = 0; j < n-1-i; j++) { // 内层循环:相邻比较
if (arr[j] > arr[j+1]) { // 升序条件
// 交换元素
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
优化版本(提前结束)
void bubbleSortOptimized(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
bool swapped = false; // 标记是否发生交换
for (int j = 0; j < n-1-i; j++) {
if (arr[j] > arr[j+1]) {
swap(arr[j], arr[j+1]);
swapped = true;
}
}
if (!swapped) break; // 如果没有交换,说明已有序
}
}
与链表版本的对比
标准冒泡排序
- 比较相邻元素:arr[j] 和 arr[j+1]
- 每轮确定一个位置,从后往前确定
链表版本(题目代码)
- 比较当前元素与后续所有元素:p 和 q
- 每轮让较小值"冒泡"到前面
- 本质上是选择排序的思想,但用了冒泡的交换方式
算法特性
时间复杂度
- 最好情况:O(n) - 数组已经有序(优化版本)
- 平均情况:O(n²)
- 最坏情况:O(n²) - 数组完全逆序
空间复杂度
- O(1) - 只需要常数个额外变量
稳定性
- 稳定排序 - 相等元素的相对位置不会改变
- 因为只有在
arr[j] > arr[j+1]
时才交换
冒泡排序的优缺点
优点
- 算法简单:容易理解和实现
- 原地排序:只需要O(1)的额外空间
- 稳定排序:不会改变相等元素的顺序
- 自适应性:对于基本有序的数组,可以提前结束
缺点
- 效率低:O(n²)的时间复杂度对大数据量不适用
- 交换次数多:需要大量的元素交换操作
- 实用性差:在实际应用中很少使用
应用场景
冒泡排序主要用于:
- 教学目的:帮助理解排序算法的基本概念
- 小规模数据:数据量很小时(n < 50)
- 基本有序数据:数组已经接近有序的情况
- 简单场景:对性能要求不高,代码简洁性更重要的场合
冒泡排序虽然效率不高,但其简单直观的特性使它成为学习排序算法的经典入门选择。