各种内部排序算法的比较及应用
这一节是全章的总账,教材开篇就说:「对各种排序算法的比较是考研常考的内容。」
比较基于五个因素:时间复杂度、空间复杂度、稳定性、适用性和过程特征。前四个查表 8.1 就行,第五个「过程特征」查不到表,只能靠理解——而它恰恰是综合题的常客。
机制
表 8.1 各种排序算法的性质
| 算法种类 | 最好 | 平均 | 最坏 | 空间复杂度 | 是否稳定 |
|---|---|---|---|---|---|
| 直接插入排序 | 是 | ||||
| 冒泡排序 | 是 | ||||
| 简单选择排序 | 否 | ||||
| 希尔排序 | — | — | — | 否 | |
| 快速排序 | 否 | ||||
| 堆排序 | 否 | ||||
| 二路归并排序 | 是 | ||||
| 基数排序 | 是 |
边界辨析:
希尔排序那一行的三个时间格子是空的,这是教材有意为之: 「因为希尔排序的时间复杂度依赖于增量函数,所以无法准确给出其时间复杂度。」 表格标题下还有一句:「其中空间复杂度仅列举了平均情况的复杂度」—— 所以快速排序那格写
,最坏是 ,表里没写但要知道。
五个因素逐条
① 从时间复杂度看
- 简单选择排序、直接插入排序和冒泡排序平均情况下时间复杂度都为
,且实现过程也较为简单,但直接插入排序和冒泡排序最好情况下的时间复杂度可以达到 ,而简单选择排序则与序列的初始状态无关。 - 希尔排序作为插入排序的拓展,对较大规模的数据都可以达到很高的效率,但目前未得出其精确的渐近时间。
- 堆排序利用了一种称为堆的数据结构,可以在线性时间内完成建堆,且在
内完成排序过程。 - 快速排序基于分治的思想,虽然最坏情况下的时间复杂度会达到
,但平均性能可以达到 ,在实际应用中常常优于其他排序算法。 - 归并排序同样基于分治的思想,但其分割子序列与初始序列的排列无关,因此它的最好、最坏和平均时间复杂度均为
。
② 从空间复杂度看
- 简单选择排序、插入排序、冒泡排序、希尔排序和堆排序都仅需借助常数个辅助空间。
- 快速排序需要借助一个递归工作栈,平均大小为
,最坏情况下可能会增长到 。 - 二路归并排序在合并操作中需要借助较多的辅助空间用于元素复制,大小为
。虽然有方法能克服这个缺点,但其代价是算法会很复杂而且时间复杂度会增加。
③ 从稳定性看
| 稳定 | 不稳定 |
|---|---|
| 插入排序、冒泡排序、归并排序、基数排序 | 简单选择排序、快速排序、希尔排序、堆排序 |
- 平均时间复杂度为
的稳定排序算法只有归并排序。 - 对于不稳定的排序算法,只需举出一个不稳定的实例即可。
- 「对于排序算法的稳定性,读者应能从算法本身的原理上去理解,而不应拘泥于死记硬背。」
④ 从适用性看
| 仅适用于顺序存储 | 顺序 + 链式都适用 |
|---|---|
| 折半插入排序、希尔排序、快速排序、堆排序 | 直接插入排序、冒泡排序、简单选择排序、归并排序、基数排序 |
关联对照:
「仅顺序」那一栏的四个算法有一个共同点:都需要按下标随机跳跃。 折半插入要折半定位、希尔要跳增量、快排要双指针对撞、堆排序要
/ 定位孩子—— 链表做不到 随机存取,所以它们都用不了。 这与 7.2.2 折半查找只能顺序存储 是同一条理由,不必单独记。
⑤ 从过程特征看
采用不同的排序算法,在一趟或几趟处理后的排序结果通常是不同的,考研题中经常出现给出一个待排序的初始序列和已部分排序的序列,问其采用何种排序算法。这就要求对各类排序算法的过程特征十分熟悉。
| 算法 | 每趟之后的特征 |
|---|---|
| 冒泡排序、简单选择排序、堆排序 | 每趟处理后都能产生当前的最大值或最小值(即已在最终位置) |
| 快速排序 | 一趟处理至少能确定一个元素的最终位置(枢轴),位置不固定 |
| 直接插入排序 | 前 |
| 希尔排序 | 相隔固定增量的元素成组有序 |
| 归并排序 | 每趟后长度为 |
选取排序算法需要考虑的因素
- 待排序的元素个数
。 - 待排序的元素的初始状态。
- 关键字的结构及其分布情况。
- 稳定性的要求。
- 存储结构及辅助空间的大小限制等。
排序算法小结
- 若
较小,可采用直接插入排序或简单选择排序。直接插入排序所需的记录移动次数较简单选择排序的多,因此当记录本身信息量较大时,用简单选择排序较好。 - 若
较大,应采用时间复杂度为 的排序算法:快速排序、堆排序或归并排序。 - 当待排序的关键字随机分布时,快速排序被认为是目前基于比较的内部排序算法中最好的算法。
- 堆排序所需的辅助空间少于快速排序,且不会出现快速排序可能的最坏情况,这两种排序都是不稳定的。
- 若要求稳定且时间复杂度为
,可选用归并排序。
- 若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。
- 在基于比较的排序算法中,每次比较两个关键字的大小之后,仅出现两种可能的转移——由此可用决策树论证:基于比较的排序算法在最坏情况下至少需要
次比较,即 是比较类排序的下界。这也是基数排序能「更快」的原因——它根本不做比较,所以不受这个下界约束。
手算模板
由中间序列判断算法(本节最高频的综合题):
- 先看有没有元素已在最终位置。
- 两端各有一段已就位 → 冒泡 / 简单选择 / 堆排序。
- 中间有一个元素左边全小右边全大 → 快速排序。
- 看前缀是否有序但位置不对 → 直接插入排序。
- 看是否相隔固定距离成对有序 → 希尔排序,距离就是增量。
- 看是否成段有序且段长为
→ 归并排序。 - 用候选算法手推一趟验证。
由需求选算法:
| 需求 | 选择 |
|---|---|
| 简单选择排序(移动少) | |
| 直接插入排序 | |
| 快速排序 | |
| 堆排序 | |
| 归并排序 | |
| 初始基本有序 | 直接插入或冒泡 |
| 只能链式存储 | 直接插入 / 冒泡 / 简单选择 / 归并 / 基数 |
边界
| 说法 | 判断 | 说明 |
|---|---|---|
| 「冒泡和简单选择的最好情况都是 | ❌ | 简单选择与初始状态无关,恒为 |
| 「希尔排序的时间复杂度是 | ⚠️ | 教材不给具体值,表里那三格是空的 |
| 「快速排序的空间是 | ❌ | |
| 「 | ❌ | 只有归并 |
| 「堆排序的空间比快排大」 | ❌ | 堆排序 |
| 「快速排序是最好的排序算法」 | ⚠️ | 限定:关键字随机分布时、基于比较的内部排序算法中 |
| 「初始基本有序时用快速排序」 | ❌ | 此时快排最坏;应选直接插入或冒泡 |
| 「 | ❌ | 记录本身信息量大时用简单选择排序(移动次数少) |
| 「堆排序适用于链式存储」 | ❌ | 仅顺序存储 |
| 「归并排序只能用于顺序存储」 | ❌ | 顺序和链式都可以 |
| 「基数排序也受 | ❌ | 它不基于比较,不受该下界约束 |
| 「稳定的算法比不稳定的好」 | ❌ | 稳定性不衡量优劣(8.1.1) |
口径差异:
算竞里排序只有一个动作:
sort();需要稳定就stable_sort()。 408 这一节的全部内容——八行表格、四类分组、过程特征——在算竞里都不产生任何决策。 但它是第 8 章分值最集中的地方:表 8.1 每一格都被单独考过。 建议把这张表默写三遍,比读三遍有用得多。
对照速查
| 记忆抓手 | 内容 |
|---|---|
| 最好能到 | 直接插入、冒泡(都要「基本有序」) |
| 与初始状态无关的 | 简单选择、堆排序、归并排序、基数排序 |
| 最坏会退化的 | 快速排序( |
| 空间不是 | 快排 |
| 稳定的四个 | 插入、冒泡、归并、基数 |
| 仅顺序存储的四个 | 折半插入、希尔、快排、堆排序 |
| 每趟产生极值的三个 | 冒泡、简单选择、堆排序 |
两句一字不差的结论:
- 「平均时间复杂度为
的稳定排序算法只有归并排序。」 - 「当待排序的关键字随机分布时,快速排序被认为是目前基于比较的内部排序算法中最好的算法。」
考点
- 表 8.1 全表——每一格都被单独考过。
- 各种排序算法的特点、比较和适用场景(2017、2020、2022 命题追踪)。
- 排序算法的稳定性判断及改进(2021、2023 命题追踪)。
- 更适合采用顺序存储的排序算法(2017 命题追踪)。
- 根据排序的中间过程判断所采用的排序算法(2009、2010 命题追踪)。
- 每趟排序后都至少能确定一个元素的最终位置的排序算法(2012 命题追踪)。
- 选取排序算法时需要考虑的因素(2019 命题追踪)。
- 排序算法小结的四条选择建议。
链接
- 🏠 返回总览:数据结构第 8 章:排序总览
- ⬅️ 上一节:8.5 归并排序、基数排序和计数排序
- ➡️ 下一节:8.7 外部排序
- 🔗 稳定性的定义:8.1.1 排序的定义
- 🔗 为什么「仅顺序存储」:7.2.2 折半查找
- 📖 名词库:第 8 章名词库