前几天给团队做内部技术分享,我把排序算法从头到尾讲了一遍。有个刚入职的同事直接问我:“现在写代码都让AI代劳了,像冒泡排序、快速排序这种东西,背得再熟还有意义吗?”
这个问题比排序本身有意思。AI确实能在几秒钟之内给你一段能跑的冒泡排序C++代码,甚至把八大排序默写一遍、注释写得比教材还漂亮。但它很难回答另一个层面的问题:为什么AI训练的数据管道里要按序列长度排序?为什么大模型采样时要先对概率排序再截断?为什么一个搜索系统召回之后还要再来一次重排序?这些场景全都离不开排序,可它们已经不是教科书里那个“把数组排好”的排序了。
所以我把这个专题命名为“大经典排序学习”,拆开来看有两层意思:一是把经典排序放进大规模数据、真实工程、AI模型训练推理的语境里重新理解,二是把排序背后的分治、比较、稳定性、优先级这些思维方式真正学透。这篇文章不打算再贴一遍八大排序的模板代码,而是想顺着“算法思维”这条主线,把排序从原理到工程、再到AI应用串一遍。无论你是后端开发、算法工程师,还是刚开始啃数据结构与算法的学生,只要能理解“让数据变得有序”本身就是一种底层能力,这篇文章应该能给你一些不一样的角度。
1. 为什么在AI时代还要啃排序算法——先澄清一个认知误区
1.1 你和AI最大的差别,在于会不会判断“该用哪种排序”
先说个我自己观察到的现象:用过AI编程工具的人应该都有同感,让它写一个排序算法,它基本不会出错。你让它用Python写一个对多维列表按某一列排序的代码,几秒钟它就能抖出一段带注释的实现。但你要是问它“我这里有100TB的日志文件,分布在几十台机器上,想取访问量最高的100个IP,怎么做”,它就很容易给出一个看起来正确、实际没法落地的方案,因为它只是在做词元概率预测,不是在真正理解你的数据规模、内存限制和排序稳定性要求。
AI能“背出”方案,但做决策的是你。就拿排序来说,一个合格工程师脑子里应该装着一张决策表:数据量是几百条还是几百万条?内存能不能放下?要不要稳定排序?比较器怎么写才合法?排序之后还要不要保持原来的相对顺序?这些问题,AI只会根据训练数据里的统计规律给你一个“大概率的答案”,而不是针对你的场景给出“最优的取舍”。所谓算法思维,核心就是这种在约束条件下做决策的能力。这也是为什么我坚持认为,越是用AI辅助写代码的时代,越要把排序这种基础算法的原理吃透,因为你至少要能判断AI给的代码在你的场景里到底对不对、要不要改。
1.2 “大经典排序学习”:从两个方向重新理解这套古老知识
排序算法可能是计算机科学里最古老的一类问题了。从1950年代开始,学术界和工业界对排序的研究就没停过,到现在我们常用的快排、归并、堆排,每一种背后都站着几十年的工程沉淀。
“大经典排序学习”不是一个官方术语,我是把它当做一个重新审视排序的框架来用的。“大”字对应的是大规模:当数据从一个数组变成一个大文件、一张数据库表、一份分布式数据集时,经典排序会被改造成什么样?外部排序、多路归并、Top-K选择、分桶排序,这些都是经典思路在“大”面前的延伸。“经典”对应的是那些基本算法本身:冒泡、插入、快排、归并、堆排、希尔、计数、基数。它们真正的价值不是让你在面试时默写代码,而是提供了一套复杂度分析的样本、一组稳定性的案例、一批“比较交换”思路的源头。
顺着这两个方向去学,排序就不再是一门死记硬背的课,而是一张可以迁移到AI数据处理、推荐系统、数据库优化、甚至大模型推理里的思维地图。
1.3 排序是AI技术栈里的隐形基础设施
很多人一说AI就想到神经网络、梯度下降、注意力机制,很少会把“排序”和AI挂钩。但实际上,排序几乎是AI技术栈里最常用的操作之一,只是它经常被封装在框架内部,不被人注意。
大模型训练阶段,数据管道的batching会按序列长度排序来减少padding浪费;强化学习里人类反馈对比数据,本质上是让模型学习对多个回答进行排序;推理阶段Top-K和Top-P采样,第一步也是把所有token的概率做一个降序排列,再决定截断到哪里;检索增强生成RAG更是明晃晃的两阶段排序:先用向量检索召回上百条候选,再做一次精细的重排序,只把最相关的几条送给大模型。除此之外,向量数据库的距离计算、推荐系统的召回排序、知识图谱里的路径选择,底层处处都是排序的身影。
如果你只把排序理解成“把一列数字从小到大排好”,那你确实很难看到它跟AI有什么关系。但如果你把排序理解成“在大量候选中找到最值得优先处理的那些”,那么AI的整个输入输出链路,几乎每一层都离不开它。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 经典排序的精髓拆解:三套不同的思维模型
2.1 O(n²)一档:插入排序才是“局部分治”的隐形冠军
很多教材讲解排序习惯从冒泡开始,但在我实际带人的经验里,冒泡排序的教学价值远大于工程价值,而插入排序才是那个“低调但有用”的家伙。
为什么这么说?先看冒泡排序的思路:从左到右反复扫描数组,相邻元素如果顺序不对就交换,每一轮把当前最大的元素“冒”到最后面。这个过程很好理解,但它的交换次数特别多,数据稍微大一点就非常吃亏。工程上几乎没有任何成熟的排序库会用纯冒泡排序,原因很简单:它除了好教、好写,没有别的优势。
插入排序就不同了。它的思路很像你打扑克牌时整理手牌:从左往右依次处理每一张牌,把它插入到左边已经有序的牌堆里正确的位置。这个算法有两个很棒的特性。第一,它天然稳定,相同元素的相对顺序不会改变。第二,它对于“近乎有序”的数据表现得异常好,如果数组已经基本有序,每个元素只需要移动一两次就能到位,这时候插入排序的时间复杂度可以逼近O(n)。也正因为这个特性,很多混合排序算法都会在递归到小规模子数组时切回插入排序,比如Java的Arrays.sort在排序元素个数小于47时就直接用插入排序,这招在Android和JDK源码里都能看到。
2.2 O(n log n)里的功臣:快排为什么能笑傲江湖
快排是绝大多数排序算法教学里的“明星”,它的核心是分治思想:从数组里挑一个基准值(pivot),把所有小于基准的元素放到左边、大于基准的放到右边,然后递归处理左右两边。这个过程像不像你整理一堆文件?先随手抽一份做标杆,把文件粗略分成“比它小的”和“比它大的”两摞,再对每摞重复同样的操作,最终每份
