排序算法详解

个人主页流年如梦

专栏《零基础轻松入门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. 稳定性:选择、希尔、堆、快排都不稳定

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

相关推荐
先吃饱再说9 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰12 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术13 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六17 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术17 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize18 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考1 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
CSharp精选营1 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法