排序算法详解

个人主页流年如梦

专栏《零基础轻松入门C语言》 《数据结构:从入门到掌握》

文章目录

前言

排序是数据结构与算法中最基础、最高频、面试必问 的内容,无论是日常开发、刷题、考试,还是底层系统设计,都离不开排序。本文根据经典学习笔记整理,涵盖插入、选择、交换、归并、非比较 五大类共八种排序,包含核心思想、完整代码、复杂度、稳定性、适用场景、易错点,一站式学会所有排序

一.排序基础概念

排序 :将一组数据按关键字递增或递减排列
稳定性 :相同值在排序后相对顺序不变 ,则为稳定排序
内部排序 :数据在内存中排序(👉本篇文章讲的就是内部排序
外部排序:数据太大,内存放不下,需要外存(如归并外排序)

二.排序算法分类

  1. 插入排序 --> 直接插入排序、希尔排序
  2. 选择排序 --> 直接选择排序、堆排序
  3. 交换排序 --> 冒泡排序、快速排序
  4. 归并排序
  5. 非比较排序 --> 计数排序

三.八大排序

3.1直接插入排序

像整理扑克牌一样,把待排序数据逐个插入到前面已经有序的序列 中,直到全部插完

c 复制代码
void InsertSort(int* a, int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int end = i;
        int tmp = a[end + 1];
        while (end >= 0)
        {
            if (a[end] > tmp)
            {
                a[end + 1] = a[end];
                end--;
            }
            else
            {
                break;
            }
        }
        a[end + 1] = tmp;
    }
}

🧐分析

时间复杂度O(N²),空间复杂度O(1)

稳定性 --> 稳定

适用场景 --> 数据量小、基本有序

3.2希尔排序(缩小增量排序)

对插入排序的优化(通常是gap=n/3+1):

  1. 选增量gap,分组进行插入排序
  2. 不断缩小gap
  3. 最后gap=1,就是直接插入排序
c 复制代码
void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1)
    {
        gap = gap / 3 + 1;
        for (int i = 0; i < n - gap; i++)
        {
            int end = i;
            int tmp = a[end + gap];
            while (end >= 0)
            {
                if (a[end] > tmp)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }
}

🧐分析

时间复杂度O(N¹·³) ~ O(N²),空间复杂度O(1)

稳定性 --> 不稳定

适用场景 --> 中等数据量,效率远高于直接插入

3.3直接选择排序

每一轮选出最小或最大值,放到对应位置,直到有序

c 复制代码
void Swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void SelectSort(int* a, int n)
{
    int begin = 0, end = n - 1;
    while (begin < end)
    {
        int mini = begin, maxi = begin;
        for (int i = begin; i <= end; i++)
        {
            if (a[i] < a[mini])
                mini = i;
            if (a[i] > a[maxi])
                maxi = i;
        }
        if (begin == maxi)
            maxi = mini;
        Swap(&a[mini], &a[begin]);
        Swap(&a[maxi], &a[end]);
        begin++;
        end--;
    }
}

🧐分析

时间复杂度O(N²),空间复杂度O(1)

稳定性 --> 不稳定

适用场景 --> 理解思路,几乎不用

3.4堆排序

利用完全二叉堆 选择数据(👉点击转跳二叉树详解):

  1. 升序 --> 建大堆
  2. 降序 --> 建小堆
c 复制代码
void AdjustDown(int* a, int n, int parent)
{
    int child = parent * 2 + 1;
    while (child < n)
    {
        if (child + 1 < n && a[child + 1] > a[child])
            child++;
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
            break;
    }
}

void HeapSort(int* a, int n)
{
    for (int i = (n - 1 - 1) / 2; i >= 0; i--)
    {
        AdjustDown(a, n, i);
    }
    int end = n - 1;
    while (end > 0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        end--;
    }
}

🧐分析

时间复杂度O(NlogN),空间复杂度O(1)

稳定性 --> 不稳定

适用场景 --> 海量数据、优先级队列、Top-K问题

3.5冒泡排序

相邻元素两两比较,大的往后"冒",可以提前优化结束

c 复制代码
void BubbleSort(int* a, int n)
{
    for (int i = 0; i < n; i++)
    {
        int exchange = 0;
        for (int j = 0; j < n - i - 1; j++)
        {
            if (a[j] > a[j + 1])
            {
                Swap(&a[j], &a[j + 1]);
                exchange = 1;
            }
        }
        if (exchange == 0)
            break;
    }
}

🧐分析

时间复杂度O(N²),最好为O(N),空间复杂度O(1)

稳定性 --> 稳定

适用场景 --> 教学演示、简单理解

3.6快速排序

分治法

  1. 选基准值key
  2. 小的放左,大的放右
  3. 递归左右区间

三种写法:Hoare、挖坑法、前后指针(最常用)

如下为**前后指针(双指针)**代码

c 复制代码
int Partion(int* a, int left, int right)
{
    int prev = left;
    int cur = left + 1;
    int keyi = left;
    while (cur <= right)
    {
        if (a[cur] < a[keyi] && ++prev != cur)
            Swap(&a[prev], &a[cur]);
        cur++;
    }
    Swap(&a[keyi], &a[prev]);
    return prev;
}

void QuickSort(int* a, int left, int right)
{
    if (left >= right)
        return;
    int keyi = Partion(a, left, right);
    QuickSort(a, left, keyi - 1);
    QuickSort(a, keyi + 1, right);
}

🧐分析

时间复杂度O(NlogN),最坏为O(N²)(有序数据),空间复杂度O(logN)

稳定性 --> 不稳定

适用场景 --> 实际最快、最常用的通用排序

3.7归并排序

分治法

  1. 拆分数组至单个元素
  2. 两两合并有序数组
c 复制代码
void _MergeSort(int* a, int left, int right, int* tmp)
{
    if (left >= right)
        return;
    int mid = (left + right) / 2;
    _MergeSort(a, left, mid, tmp);
    _MergeSort(a, mid + 1, right, tmp);

    int begin1 = left, end1 = mid;
    int begin2 = mid + 1, end2 = right;
    int index = left;

    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
            tmp[index++] = a[begin1++];
        else
            tmp[index++] = a[begin2++];
    }
    while (begin1 <= end1)
        tmp[index++] = a[begin1++];
    while (begin2 <= end2)
        tmp[index++] = a[begin2++];

    for (int i = left; i <= right; i++)
        a[i] = tmp[i];
}

void MergeSort(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    _MergeSort(a, 0, n - 1, tmp);
    free(tmp);
}

🧐分析

时间复杂度O(NlogN),空间复杂度O(N)

稳定性 --> 稳定

适用场景 --> 链表排序、外部排序、要求稳定且要效率

3.8计数排序(非比较排序)

统计每个数字出现次数,按范围回写,只适用于整数、范围集中

c 复制代码
void CountSort(int* a, int n)
{
    int min = a[0], max = a[0];
    for (int i = 0; i < n; i++)
    {
        if (a[i] < min) min = a[i];
        if (a[i] > max) max = a[i];
    }
    int range = max - min + 1;
    int* count = (int*)calloc(range, sizeof(int));

    for (int i = 0; i < n; i++)
        count[a[i] - min]++;

    int j = 0;
    for (int i = 0; i < range; i++)
    {
        while (count[i]--)
            a[j++] = i + min;
    }
    free(count);
}

🧐分析

时间复杂度O(N+range),空间复杂度O(range)

稳定性 --> 稳定

适用场景 --> 年龄、分数、成绩、范围集中整数

四.排序复杂度与稳定性总结表

排序 平均 最好 最坏 空间 稳定性
冒泡 O(N2) O(N) O(N2) O(1) 稳定
直接插入 O(N2) O(N) O(N2) O(1) 稳定
直接选择 O(N2) O(N2) O(N2) O(1) 不稳定
希尔 O(N1.3) O(N) O(N2) O(1) 不稳定
堆排序 O(NlogN) O(NlogN) O(NlogN) O(1) 不稳定
快速排序 O(NlogN) O(NlogN) O(N2) O(logN) 不稳定
归并排序 O(NlogN) O(NlogN) O(NlogN) O(N) 稳定
计数排序 O(N+range) O(N+range) O(N+range) O(range) 稳定

五.排序算法适用场景速选

  1. 数据小、基本有序 --> 直接插入排序
  2. 追求最快、通用场景 --> 快速排序
  3. 要求稳定、效率高 --> 归并排序
  4. 整数、范围集中 --> 计数排序
  5. 海量数据、内存有限 --> 堆排序
  6. 链表排序 --> 归并排序
  7. 教学演示、最简单 --> 冒泡、选择

🎯总结

排序是算法基石,快排、堆排、归并是面试三巨头

  1. 简单排序:冒泡、插入、选择,好理解但效率低
  2. 高效排序:快排、堆排、归并O(NlogN)
  3. 特殊排序:计数排序,非比较、极快但场景受限

⚠️易错点

  1. 快排 在有序或重复数据多会退化到O(N^2)
  2. 堆排序升序必须建大堆,千万不要搞反
  3. 直接选择 要注意maxibegin重叠问题
  4. 归并排序 需要额外O(N)空间
  5. 希尔排序 gap最后必须等于1
  6. 计数排序数据范围太大会浪费大量内存
  7. 稳定性:选择、希尔、堆、快排都不稳定

👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步

相关推荐
会编程的土豆1 小时前
Go 语言中的 `new` 关键字(创建指针)
java·算法·golang
南宫萧幕2 小时前
HEV能量管理建模实战:从零搭建 Simulink 物理环境到 Python(DQN) 强化学习联合仿真调通
开发语言·python·算法·matlab·汽车·控制
x_yeyue2 小时前
2026第十七届蓝桥杯c++B组省赛题解
笔记·算法·蓝桥杯·acm·题解
handler012 小时前
【C++ 算法竞赛基础】数论篇:核心公式、经典例题与高频模板
开发语言·c++·算法·蓝桥杯·数论·最大公约数·最小公倍数
z200509302 小时前
今日算法(二叉树)
数据结构·c++·算法
南境十里·墨染春水2 小时前
八大排序算法 - 基数排序
算法·排序算法
老四啊laosi2 小时前
[滑动窗口] 12. 将 x 减到 0 的最小操作数
算法·leetcode·将 x 减到 0 的最小操作数
一条大祥脚2 小时前
Codeforces Round 1098 (Div. 2)
算法·深度优先
时空自由民.2 小时前
平衡车PID控制系统(豆包版本)
算法