基础排序算法详解:冒泡排序、选择排序与插入排序

引言

上一章,我们聊到了排序的基本概念和常见算法的分类。这一次,我们从基础开始,深入剖析三种常见的O(n²) 排序算法:冒泡排序选择排序插入排序

它们是学习排序算法的入门神器,不仅实现简单,还能帮助你掌握排序的核心思想。虽然它们的效率较低,但在小规模数据场景中仍然非常实用。

准备好了吗?让我们一起"搞懂这三兄弟"!

一、冒泡排序(Bubble Sort)

算法思想

冒泡排序通过重复比较相邻元素,将较大的元素逐步向右"冒泡"。每一轮都把未排序部分的最大值放到最后。

算法过程
  1. 从第一个元素开始,依次比较相邻元素,如果左边的比右边大,就交换它们。
  2. 重复这一过程,直到所有元素有序。
cpp 复制代码
​
​
#include <stdio.h>

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {  // 外层循环控制遍历轮数
        int swapped = 0;              // 标记是否发生交换
        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;
                swapped = 1;
            }
        }
        if (!swapped) break;  // 如果没有发生交换,提前结束排序
    }
}

int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    bubbleSort(arr, n);

    printf("排序后的数组: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

​

​
优化版:提前结束判断

通过引入swapped变量,检测一轮比较后是否发生交换。如果没有交换,说明数组已经有序,可以提前结束循环,提升效率。

复杂度分析
  • 时间复杂度:最优 O(n)(已排序),最差 O(n²),平均 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定

二、选择排序(Selection Sort)

算法思想

选择排序的核心是"选择最小值":每一轮从未排序部分找到最小值,将它与当前轮的起始位置交换。

算法过程
  1. 遍历未排序部分,找到最小元素。
  2. 将最小元素与当前轮的起始位置交换。
  3. 重复以上步骤,直到排序完成。
cpp 复制代码
​
#include <stdio.h>

void selectionSort(int arr[], int n) {
    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[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    selectionSort(arr, n);

    printf("排序后的数组: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

​
复杂度分析
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定(因为交换可能改变相等元素的相对顺序)
优缺点
  • 优点:实现简单,数据量小时可用。
  • 缺点:不稳定,效率较低。

三、插入排序(Insertion Sort)

算法思想

插入排序通过逐步构建已排序部分,将未排序部分的元素插入到正确位置。它的核心思想类似于打牌时整理手牌。

算法过程
  1. 从第一个元素开始,它可以认为是有序的。
  2. 取下一个元素,与已排序部分从后往前比较,找到合适的位置插入。
  3. 重复,直到所有元素有序。
cpp 复制代码
​
#include <stdio.h>

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];  // 当前待插入的元素
        int j = i - 1;
        // 向右移动已排序部分,直到找到合适的位置
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;  // 插入到正确位置
    }
}

int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    insertionSort(arr, n);

    printf("排序后的数组: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

​
复杂度分析
  • 时间复杂度:最优 O(n)(部分有序),最差 O(n²),平均 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定
适用场景

插入排序在数据量小、数据部分有序时非常高效。


四、对比与总结

排序算法 时间复杂度(平均) 空间复杂度 稳定性 特点
冒泡排序 O(n²) O(1) 稳定 简单易懂,但效率较低
选择排序 O(n²) O(1) 不稳定 不适合对稳定性有要求的场景
插入排序 O(n²) O(1) 稳定 数据量小或部分有序时性能较优

五、预告

通过这篇文章,我们详细学习了三种基础排序算法的原理与实现。它们是排序算法的基础,帮助我们理解排序的核心思想。在接下来的文章中,我们将进入高级排序算法的世界,从快速排序开始,感受分治法的强大威力,敬请期待!


结语

冒泡排序、选择排序、插入排序是排序算法的"入门三剑客",它们简单易懂,却能揭示许多排序算法的本质。希望通过这篇文章,你能深入理解它们的逻辑与实现,并为接下来的高级排序算法打下坚实的基础。

有什么问题或建议,欢迎评论区讨论,我们一起进步!🎉

相关推荐
czy87874754 分钟前
const 在 C/C++ 中的全面用法(C/C++ 差异+核心场景+实战示例)
c语言·开发语言·c++
咖丨喱10 分钟前
IP校验和算法解析与实现
网络·tcp/ip·算法
罗湖老棍子23 分钟前
括号配对(信息学奥赛一本通- P1572)
算法·动态规划·区间dp·字符串匹配·区间动态规划
fengfuyao9851 小时前
基于MATLAB的表面织构油润滑轴承故障频率提取(改进VMD算法)
人工智能·算法·matlab
机器学习之心1 小时前
基于随机森林模型的轴承剩余寿命预测MATLAB实现!
算法·随机森林·matlab
一只小小的芙厨1 小时前
寒假集训笔记·树上背包
c++·笔记·算法·动态规划
庄周迷蝴蝶1 小时前
四、CUDA排序算法实现
算法·排序算法
以卿a1 小时前
C++(继承)
开发语言·c++·算法
I_LPL1 小时前
day22 代码随想录算法训练营 回溯专题1
算法·回溯算法·求职面试·组合问题
金融RPA机器人丨实在智能1 小时前
2026动态规划新风向:实在智能Agent如何以自适应逻辑重构企业效率?
算法·ai·重构·动态规划