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

引言

上一章,我们聊到了排序的基本概念和常见算法的分类。这一次,我们从基础开始,深入剖析三种常见的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) 稳定 数据量小或部分有序时性能较优

五、预告

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


结语

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

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

相关推荐
爱coding的橙子4 分钟前
每日算法刷题Day24 6.6:leetcode二分答案2道题,用时1h(下次计时20min没写出来直接看题解,节省时间)
java·算法·leetcode
慢慢慢时光5 分钟前
leetcode sql50题
算法·leetcode·职场和发展
pay顿6 分钟前
力扣LeetBook数组和字符串--二维数组
算法·leetcode
精神小伙mqpm8 分钟前
leetcode78. 子集
算法·深度优先
岁忧8 分钟前
(nice!!!)(LeetCode每日一题)2434. 使用机器人打印字典序最小的字符串(贪心+栈)
java·c++·算法·leetcode·职场和发展·go
dying_man11 分钟前
LeetCode--18.四数之和
算法·leetcode
HappyAcmen14 分钟前
1.3 古典概型和几何概型
笔记·概率论·学习方法
无敌的小笼包20 分钟前
第四讲:类和对象(下)
数据结构·c++
知识漫步37 分钟前
代码随想录算法训练营第60期第五十九天打卡
算法
分形数据1 小时前
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
算法·mathematica·复分析