基础排序算法介绍(一)总论以及冒泡算法介绍
一 修订记录
| 序号 | 修订内容 | 修订时间 |
|---|---|---|
| 1 | 新增 | 20251029 |
二 排序算法介绍
排序算法比较表
🔄 比较排序算法
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 适用场景 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | ✅ 稳定 | 教学、小规模或基本有序数据 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | ❌ 不稳定 | 大规模通用数据、内存敏感 |
| 简单选择排序 | O(n²) | O(n²) | O(1) | ❌ 不稳定 | 小规模数据、交换次数需少 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | ❌ 不稳定 | 大数据量、需要保证最坏性能 |
| 直接插入排序 | O(n²) | O(n²) | O(1) | ✅ 稳定 | 小规模、基本有序数据、链表排序 |
| 希尔排序 | O(n log n) - O(n²) | O(n²) | O(1) | ❌ 不稳定 | 中等规模数据 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | ✅ 稳定 | 大数据量、稳定性要求高、外部排序、链表排序 |
🎯 非比较排序算法
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 适用场景 |
|---|---|---|---|---|---|
| 计数排序 | O(n + k) | O(n + k) | O(n + k) | ✅ 稳定 | 非负整数、数据范围k较小 |
| 桶排序 | O(n + k) | O(n²) | O(n + k) | ✅ 稳定 | 数据均匀分布、浮点数排序 |
| 基数排序 | O(n × k) | O(n × k) | O(n + k) | ✅ 稳定 | 整数或字符串排序 |
三 详细介绍
下面从 算法原理、复杂度分析、使用场景、代码实现 详细介绍各排序算法
3.1 冒泡排序
冒泡排序是一种基础且直观的排序算法,其核心思想是通过重复遍历待排序序列,比较相邻元素,并根据需要交换位置,使较大(或较小)的元素逐渐从序列一端“浮”到另一端,如同气泡上升,故此得名。下面我们将从原理、复杂度、使用场景、代码实现及与其他算法的比较等方面,对其进行详细说明。
3.1.1 算法原理
冒泡排序属于交换排序的一种,其过程可以比喻为水中的气泡上浮:较轻的气泡(较小的元素)会逐步交换到序列的前端(升序排序时)。算法的具体步骤如下:
1.比较相邻元素: 从序列起始位置开始,比较第一和第二个元素,如果顺序错误(例如在升序排序中前一个元素大于后一个),则交换它们的位置。
2.移动并重复: 接着比较第二和第三个元素,以此类推,直到处理到序列的最后两个元素。这样一趟遍历完成后,最大的元素就会像气泡一样“浮”到序列的末尾。
3.缩小范围并重复:忽略已经排序好的末尾元素,对剩余未排序序列重复上述过程,直到整个序列有序。通常需要进行 n-1 趟这样的遍历(n为序列长度)。
示例: 对数组 [5, 2, 9, 1, 5, 6]进行升序排序。
-
第一趟遍历:比较并交换后,最大值9沉底,序列变为 [2, 5, 1, 5, 6, 9]。
-
后续遍历:每趟都确定一个当前最大元素的位置,直到所有元素有序。
一个重要的优化是设置交换标志位。如果某一趟遍历中没有发生任何交换,说明序列已经有序,算法便可提前终止,避免不必要的比较
3.1.2 复杂度
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 最好时间复杂度 | O(n) | 当输入序列已经是有序时,通过优化(标志位),仅需一趟遍历(n-1次比较)即可确认,此时效率最高。 |
| 最坏时间复杂度 | O(n²) | 当输入序列完全逆序时,需要进行 n-1 趟遍历,总的比较和交换次数约为 n(n-1)/2,属于平方级别。 |
| 平均时间复杂度 | O(n²) | 考虑到数据随机分布时,平均性能仍与 n² 成正比。 |
| 空间复杂度 | O(1) | 冒泡排序是原地排序算法,只需要常数级别的额外临时空间用于元素交换,不随数据规模 n 增大而增加。 |
| 稳定性 | 稳定 | 当比较相邻元素时,只有在顺序错误时才交换。因此,相等的元素在排序后能保持其原有的相对顺序。 |
3.1.3 使用场景
尽管冒泡排序的时间复杂度较高,不适合处理大规模数据,但在以下特定场景中仍有其价值
- 小规模数据排序:当待排序元素数量很少(例如几个到几十个)时,其实现简单易懂的优势凸显,性能开销也可接受。
- 数据基本有序:如果序列已经大部分有序,通过优化算法可以很快完成排序。
- 教学与算法入门:由于其逻辑清晰,常被用作学习排序算法思想和编程练习的入门案例。
- 特定应用场景:在某些嵌入式系统或资源极度受限的环境下,代码简洁性比绝对效率更重要时可能会被考虑。
3.1.4 代码实现
#include <stdio.h>
#include <stdbool.h> // 用于bool类型
// 冒泡排序函数
void bubbleSort(int arr[], int n) {
int i, j;
bool swapped; // 交换标志位
for (i = 0; i < n - 1; i++) {
swapped = false; // 初始化为未交换
// 最后i个元素已经排好序,无需再比较
for (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;
}
}
}
// 打印数组函数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 主函数测试
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前的数组: \n");
printArray(arr, n);
bubbleSort(arr, n);
printf("排序后的数组: \n");
printArray(arr, n);
return 0;
}
代码关键点解释:
- 外层循环 i控制排序的趟数(最多 n-1 趟)。
- 内层循环 j负责每一趟中相邻元素的比较和可能的交换。
- swapped标志位是优化关键。若某一趟内层循环未发生交换,则 swapped保持 false,通过 if (!swapped) break;提前终止排序,节省不必要的计算。
3.1.5 总结
总的来说,冒泡排序是一种概念简单、编码容易的稳定排序算法,其原地排序的特性使其空间效率高(O(1))。然而,其O(n²)的时间复杂度决定了它不适用于处理大规模数据。它的价值主要体现在教学演示和对极小规模或近乎有序的数据进行排序的场景。理解冒泡排序是学习更高效、更复杂排序算法的一个良好起点。
posted on 2025-10-29 17:06 weiwei2021 阅读(35) 评论(0) 收藏 举报
浙公网安备 33010602011771号