很多人觉得 AI 时代嘛,不懂排序也能靠现成函数干活,但真正上手做推荐、做搜索、做大模型应用时才发现,排序几乎是无处不在的“隐形基础设施”。我经常在面试和带新人时强调一个观点:语言和框架可以很快换,算法思维才是真正值钱的部分,而排序算法又是算法思维里最适合当切入点的内容。它不要求高深的数学背景,却能把数据结构、复杂度分析、边界条件、工程取舍全部串起来。这篇文章我就把自己在刷题、做系统和带新人过程中积累的排序经验写出来,照着练,比单纯背代码有用得多。
1. 为什么 AI 时代反而要回头啃排序
1.1 从 LLM 应用看排序无处不在
很多学 AI 的同学一开始会有一个错觉:现在大模型都能直接生成答案了,底层排序算法是不是就退出历史舞台了?实际恰恰相反。一个典型的 RAG 应用也好,Agent 任务规划也好,大量工作都在做“排序”:检索回来的文档要按相关性排序,多个工具调用的候选结果要按可靠性排序,甚至对话历史里的上下文片段也经常要按时间或重要程度重新排列。就算模型本身能处理语义,程序层面仍然需要一个明确、稳定、可预期的排序逻辑来控制数据的流动顺序。
再往底层看,训练数据清洗、特征工程、评估指标计算,也都离不开排序。大模型评测里常用到的 Recall@K、NDCG 这类指标,核心就是“先排序,再看前 K 个里命中了多少”。如果连排序都写不好,指标算出来也是错的。所以 AI 时代不是不需要排序,而是排序成为了更关键的中间层。掌握排序的人,看系统结构会比别人清晰很多。
1.2 排序能帮你建立真正的复杂度直觉
我认为算法思维里最值钱的东西不是会背某段代码,而是能一眼判断一套逻辑在数据量放大后是否还能跑得动。排序正好是训练这种直觉的绝佳材料。
同一个排序需求,用插入排序和用快速排序,在 10 个元素时几乎看不出差别,但数据量到 100 万时就会差出几千倍。这种“量级感”如果不亲手算一算、不亲自压一次数据,很难真正长在脑子里。我见过不少工程师写代码时只顾功能不管数据规模,最后线上数据一涨,接口直接超时,查下来往往就是某个冒泡式的双重循环在作怪。学排序的时候多想一想“最坏情况”“平均情况”“额外空间”,以后设计任何系统都会下意识去估算复杂度,这就是算法思维带来的复利。
1.3 所谓大经典,到底应该抓住什么主线
现在一提到经典排序,很多人会说是“八大排序”或“十大经典排序”。其实数量不重要,重要的是背后几条主线。按我的理解,可以分成四条主线:第一组是冒泡、选择、插入,适合理解最朴素的比较排序;第二组是希尔、归并、快排,核心是“减少比较次数”或“分而治之”;第三组是堆排序,本质是一种基于优先队列的选择排序优化;第四组是计数、桶、基数,它们跳出了“比较”这个框架,用空间和分布换时间。
把这些串起来后,你再看各种排序需求时,脑子里浮现的就不是一个个孤立的算法名,而是一棵决策树:数据量多大、数值范围多集中、是否需要稳定、内存够不够、能不能利用多线程。下面我先放一张速查表,适合放在手边随时看。
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 额外空间 | 稳定性 | 主要特点 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 实现简单,适合小数据 |
| 选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 交换次数少 |
| 插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 对接近有序的数据极快 |
| 希尔排序 | O(n^1.3~1.5) | O(n²) | O(1) | 不稳定 | 跨过远距离逆序 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 稳定且适合外部排序 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 工程应用最广 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | TopK 问题好手 |
| 计数排序 | O(n+k) | O(n+k) | O(k) | 稳定 | 只适合范围小的整数 |
| 桶排序 | O(n+k) | O(n²) | O(n+k) | 稳定 | 依赖数据分布均匀 |
| 基数排序 | O(d(n+k)) | O(d(n+k)) | O(n+k) | 稳定 | 按位分配,适合定长数据 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 把六大比较类排序练到能默写的实操方法
2.1 冒泡、选择、插入:先写好最容易出错的三个边界
很多人觉得冒泡排序太基础不值得写,但真让他在白板上手写一次,能一次过的人并不占多数。冒泡的核心是每一轮把当前未排序区间里的最大值“冒”到最后,所以内层循环的结束条件必须是长度减一再减轮数。写错边界是新手最常见的,要么多比一次,要么漏掉最后的元素。
我建议不要只背写法,而是把三个算法放到一起对比。选择排序每一轮找最小值的下标,找到后才交换,它能把交换次数降到 O(n),不过因为“远距离交换”会把相同元素的相对顺序打乱,所以它不稳定。插入排序则是“从后往前挪位置”,像一个打扑克的人把新摸到的牌插进手里已经排好的牌中。它对“基本有序”的数据效率极高,这也是后面希尔排序能成立的前提。
这里给出一份极简的插入排序实现,我特别喜欢用它来热身:
python复制def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
