基础排序算法介绍(一)总论以及冒泡算法介绍

一 修订记录

序号 修订内容 修订时间
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)    收藏  举报