用一个 “体育课排队” 的故事,结合代码手把手带你理解三大基础排序算法。一句话核心:排序就是让混乱的数据像队伍一样按高矮站好!👇
📣 一、冒泡排序:相邻比较,大个子往后站
体育课故事:体育老师让10个同学随机站队(数组),要求从左到右从矮到高排队。老师从队首开始:
- 第1轮:老师让第1和第2个同学比身高,如果左边>右边,两人交换位置;接着第2和第3比、第3和第4比……比完10人后,最高的人一定站在最右边(像气泡浮到水面)
- 第2轮:同样方式比前9人(最后1人已排好),次高的人站到倒数第二
- 重复直到全部排好
Java代码实现(含优化:若某轮无交换则提前结束):
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean 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; // 本轮无交换?提前结束!
}
}
public static void main(String[] args) {
int[] heights = {160, 155, 175, 170, 165};
bubbleSort(heights); // 排序后:155, 160, 165, 170, 175
}
}
关键特性:
- ⏱️ 时间复杂度:平均O(n²),最好O(n)(已有序时)
- 📦 空间复杂度:O(1)(原地排序)
- ⚖️ 稳定性:相等元素不交换 → 稳定排序
- 🔧 适用场景:小规模数据或基本有序的序列
🔍 二、选择排序:老师点名,最矮的出列
体育课故事:老师换了一种排队方式:
- 第1轮:老师扫视全部10人,找到最矮的同学,让他和队首交换
- 第2轮:在剩余9人中找最矮的,让他和第二个位置交换
- 重复直到全部排好
Java代码实现:
public class SelectionSort {
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIndex = i; // 假设当前位置是最矮的
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j; // 找到更矮的,更新索引
}
}
// 将最小元素交换到当前位置
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
public static void main(String[] args) {
int[] heights = {160, 155, 175, 170, 165};
selectionSort(heights); // 排序后:155, 160, 165, 170, 175
}
}
关键特性:
-
⏱️ 时间复杂度:固定O(n²)(无论数据是否有序)
-
📦 空间复杂度:O(1)(原地排序)
-
⚠️ 稳定性:交换可能破坏相等元素顺序 → 不稳定排序
例:
[5a, 2, 5b]→ 第一轮交换后:[2, 5a, 5b]→5a和5b顺序变了 -
🔧 适用场景:数据量小且交换成本较高的场景(如磁盘存储)
🃏 三、插入排序:扑克牌式插入,新牌找位置
体育课故事:老师让学生逐个入队:
-
第1步:第1个同学直接站好(已排序区:
[155]) -
第2步:第2个同学(160)和155比 → 160>155 → 站155右边 →
[155, 160] -
第3步:第3个同学(175)比160高 → 站160右边 →
[155,160,175] -
第4步:第4个同学(170)从右向左比:
- 170 < 175?→ 175向右挪 →
[155,160,空,175] - 170 > 160?→ 站160右边 →
[155,160,170,175]
- 170 < 175?→ 175向右挪 →
Java代码实现:
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) { // 从第2个元素开始
int key = arr[i]; // 当前要插入的元素
int j = i - 1;
// 向左找插入位置,比key大的右移
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]; // 大元素右移
j--;
}
arr[j + 1] = key; // 插入到正确位置
}
}
public static void main(String[] args) {
int[] heights = {160, 155, 175, 170, 165};
insertionSort(heights); // 排序后:155, 160, 165, 170, 175
}
}
关键特性 :
- ⏱️ 时间复杂度:平均O(n²),最好O(n)(已有序时)
- 📦 空间复杂度:O(1)(原地排序)
- ⚖️ 稳定性:相等时不后移 → 稳定排序
- 🔧 适用场景:基本有序的小数据或链表结构(插入成本低)
📊 终极总结:三大排序算法对比
| 特性 | 冒泡排序 | 选择排序 | 插入排序 |
|---|---|---|---|
| 时间复杂度 | O(n²) | O(n²) | O(n²) |
| 最好情况 | O(n)(优化后) | O(n²) | O(n)(已有序) |
| 空间复杂度 | O(1) | O(1) | O(1) |
| 稳定性 | ✅ 稳定 | ❌ 不稳定 | ✅ 稳定 |
| 交换次数 | 多(相邻交换) | 少(每轮1次) | 中(移动插入) |
| 适用场景 | 小数据、教学用途 | 交换成本高的场景 | 基本有序的小数据 |
⚡ 一句话选型建议:
- 数据量小且要稳定 → 插入排序
- 数据量小但交换成本高 → 选择排序
- 数据基本有序 → 冒泡排序(优化版)
- 数据量大?请用 快速排序(O(n log n))4
💡 思考题(检验理解)
int[] data = {5, 2, 9, 1, 5, 6};
-
用插入排序处理时,当
key=第二个5时,是否会移动前一个5?
→ 不会(稳定性:相等不后移) -
用选择排序处理时,两个
5的顺序是否会变?
→ 可能变(若后面的5被选为最小元素) -
用冒泡排序优化版,需要几轮完成排序?
→ 实际3轮(第4轮无交换提前结束)
记住:算法没有绝对好坏,理解场景才能选出最佳方案! 🌟