1. AI时代,为什么所有人都在回头补排序
前几天逛技术社区,看到有人在问"现在大模型写代码这么强,手写排序到底还有没有意义",底下吵了几百条。有说早该淘汰的,有说面试必考绕不开的,还有人说排序是数据结构的门面不能丢。说实话,这种争论本身挺有意思的,因为它默认了一件事——排序算法是算法领域最基本的一块试金石。
真正让我想写这篇东西的原因,是我日常帮团队做技术方案评审和培训时,发现一个很普遍的现象:很多工程师用框架、调大模型接口、写业务逻辑都很顺,但一涉及到数据怎么组织、怎么减少比较、怎么利用局部性、为什么某段代码在高并发下会退化,就明显吃力。这些问题的底层,几乎都能追溯到对排序、查找、哈希这些基础算法的理解深度上。
尤其近几年大模型爆发,AI Agent、RAG、向量检索这些东西满天飞,看起来跟传统排序算法没什么关系。但只要往深处扒一层,你会发现向量召回之后要重排,重排之后要取Top-K,知识库里的文档要先按相关性过滤再排序,模型训练数据要做采样打散,流式数据要按时间窗口排序聚合——哪一个环节少得了排序思维?
所以这篇内容我会站在一个偏实践的角度,把经典排序重新讲一遍。不是照着教科书背复杂度表,而是从工程设计、AI场景、面试考察三个维度去拆:这些算法到底解决什么问题、为什么有那么多变体、真实业务里该怎么选、遇到性能问题怎么排查。适合刚入门数据结构与算法的新手建立框架,也适合写了几年业务代码想系统补底子的工程师。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先把排序家族分个类:记忆远比背代码重要
2.1 四个关键词:稳定、原地、最好、最坏
要真正掌握一大类排序算法,光靠死记硬背代码没有意义,几天就忘了。我建议先建立一套分类维度,任何排序算法丢过来,你都能从稳定性、空间复杂度、最好情况、最坏情况、数据敏感程度这几个角度描述它,这样你才真正理解了它的本质。
先解释几个术语。
稳定性很重要,但是很多人并没有真正理解。稳定是指相同键值元素在排序前后的相对顺序保持不变。听上去抽象,举个例子就清楚了:按成绩排一个学生列表,如果两个人的分数一样,你希望学号小的人排在前面,这时候稳定的排序算法会保住这个顺序,不稳定的排序算法则会打乱。真实场景中,多条件排序就建立在稳定性之上——先按一个字段排,再用稳定排序按另一个字段排,两次排序就能精确还原业务规则。
原地排序指的是额外空间需求是常量级的,不随数据规模增长。这个对内存敏感的系统很关键。快排和堆排都是原地排序,归并排序不是——它通常需要额外的数组空间。
最好情况和最坏情况也很好理解:有些算法对输入数据的顺序很敏感,比如冒泡排序在最坏情况下要比较约n的平方次,但若数据本来就基本有序,冒泡可以提前结束,最好时间复杂度接近线性。
用这套维度去看任何排序算法,根本不需要刻意背,代码自然就记住了。
2.2 比较排序的复杂度天花板:nlogn是怎么来的
既然要聊经典排序,不得不回答一个底层问题:为什么快排、归并、堆排的平均复杂度都是O(n log n),而不是能更快?
这里的推导并不复杂。任何基于比较的排序,本质上都是在做决策,每次比较最多得到两个结果:大于或者小于等于。那排序n个元素的整个过程,相当于在一棵决策树上游走,树上有n个左右分支,至少需要能容纳n的阶乘种排列情况。假设树高为h,那么2的h次方至少要不小于n的阶乘。根据斯特林近似,可以推出h的量级就是n log n。
这就是信息论给出的限制,比较排序不可能突破这个天花板。这也是为什么你会看到当需要更高性能时,工程师会想办法绕开"比较",走计数排序、桶排序、基数排序这类非比较排序的路子,它们在某些条件下能达到线性复杂度。脑子里有了这个框架之后,任何排序算法放在你面前,你一眼就能看出它处在哪个量级区间,再也不会出现"这个算法为什么能突破nlogn"的困惑。
3. 经典算法逐个解剖:从冒泡到堆排的演进逻辑
3.1 冒泡排序与选择排序:教学价值大于实用价值
冒泡排序常被当作算法启蒙第一课。它的思路特别直白——从前往后遍历,相邻元素两两比较,如果顺序不对就交换。每一轮结束,最大的元素就像气泡一样冒到末尾。实测下来,最简单的写法大概长这样:
c复制void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
注意这个实现还有优化空间:如果某轮遍历中没有
