Skip to content

七、排序(第 79-94 题) ​

79. 以下排序算法中,平均时间复杂度为O(n log n)的是( ) ​

标签:【互联网/国企笔试超高频】

A. 冒泡排序 B. 快速排序 C. 插入排序 D. 选择排序

答案:B

【结论】 快速排序平均时间复杂度为 O(n log n),选 B。

【逐项辨析】

  • A 冒泡排序:平均 O(n²)。
  • B 快速排序:正确。 平均 O(n log n),是比较排序中实际性能最好的算法之一。
  • C 插入排序:平均 O(n²)。
  • D 选择排序:平均 O(n²)。

【知识点】 常见排序的平均复杂度对照

排序算法平均复杂度是否达到下界 O(n log n)
冒泡排序O(n²)❌
插入排序O(n²)❌
选择排序O(n²)❌
快速排序O(n log n)✅
归并排序O(n log n)✅
堆排序O(n log n)✅

O(n log n) 是基于比较的排序算法的理论下界 —— 由决策树模型可证:比较排序至少需要 ⌈log₂(n!)⌉ ≈ n log₂n 次比较。

【记忆锚点】 “只有快排、归并、堆排序达到 n log n,冒泡、插入、选择都停在 n²”。

【易混对比】 注意题干问的是“平均” —— 快排最坏会退化为 O(n²)(见第 81 题)。三个 O(n log n) 的算法中,只有归并和堆排序在所有情况下都是 O(n log n),快排的最坏情况是例外。

【自测】 下列哪个排序算法平均复杂度不是 O(n log n)? A. 快速排序 B. 归并排序 C. 堆排序 D. 简单选择排序

答:D。简单选择排序平均为 O(n²)。与第 80 题连考。

【知识关联】

  • 同库连考:与第 80 题(最坏最优=归并)、第 81 题(快排最坏 O(n²))、第 91 题(任何情况都不退化 O(n²) 的是归并/堆)构成「排序复杂度」决策组;与第 82/83 题(稳定性)双维度交叉;与第 2 题(复杂度取决于规模与初态)总纲相连——快排平均 n log n、最坏 n² 正是「初态改变复杂度」的典型。
  • 工程实现:Java 基本类型 Arrays.sort 用双轴快排(平均 n log n);对象类型用 TimSort(归并+插入,保证稳定且最坏 n log n);C++ std::sort 是 introsort(快排+堆排兜底),避免最坏 n²。工程选型看平均,但必须给最坏兜底。
  • 408 / 国企真题:408 与国网排序章必考「下列平均为 O(n log n) 的是」;注意题干若改成「最好/最坏」答案会变(冒泡最好 O(n),快排最坏 O(n²))。国企题库还常考「比较排序下界 O(n log n)」的判断。
  • 面试追问:①「为什么 O(n log n) 是比较排序下界?」(决策树模型,n! 个叶子,树高 ≥ log₂(n!));②「归并/堆排平均也是 n log n,为何常推快排?」(常数小、缓存局部性好);③「哪些排序能突破下界?」(基数/计数/桶等非比较排序)。

【拓展延伸】

变式问法:①「平均 O(n log n) 的是?」(快排,本题;归并/堆排若出现也是对的,看选项);②「冒泡平均?」(O(n²));③「比较排序平均下界?」(O(n log n))。

工程映射:JDK/C++/Python(Timsort)排序实现的复杂度合同都是「平均/最坏有明确上界」;基准测试要分 random/sorted/reverse 三组数据,正是为了看见平均与最坏的落差。

易错提醒:本题只给了快排一个 n log n 选项;若选项同时出现归并/堆排而题干问「平均」,三者都对,需看是否是多选或题干限定。另勿把「平均 n log n」误读成「最坏 n log n」——快排最坏会掉到 n²。


80. 以下排序算法中,最坏情况下时间复杂度最低(最优)的是( ) ​

标签:【互联网/国企笔试超高频】

A. 归并排序 B. 快速排序 C. 冒泡排序 D. 选择排序

答案:A

【结论】 归并排序最坏为 O(n log n),是四者中最优的,选 A。

【逐项辨析】

  • B 快速排序:最坏 O(n²)(数据已有序时)。
  • A 归并排序:正确。 最好、平均、最坏均为 O(n log n)。
  • C 冒泡排序:最坏 O(n²)。
  • D 选择排序:最坏 O(n²)。

【知识点】 三种情况的完整对照

排序算法最好平均最坏空间稳定
快速排序O(n log n)O(n log n)O(n²)O(log n)❌
归并排序O(n log n)O(n log n)O(n log n)O(n)✅
冒泡排序O(n)O(n²)O(n²)O(1)✅
选择排序O(n²)O(n²)O(n²)O(1)❌

归并排序的复杂度与数据初始状态无关 —— 因为它总是“分成两半 → 递归排序 → 合并”,分治结构与数据内容无关。

【记忆锚点】 “归并稳如泰山,快排怕有序”——归并三种情况都是 n log n,快排最坏会掉到 n²。

【易混对比】 与第 91 题同考点(任何情况下都不退化为 O(n²) 的排序),与第 86 题连考(归并的空间代价是 O(n))。“最坏情况最优”是归并排序最突出的标签。

【自测】 下列排序算法中,最坏情况时间复杂度为 O(n log n) 的有哪些?

答:归并排序、堆排序(快排最坏 O(n²);冒泡、插入、选择也都有 O(n²) 的最坏情况)。与第 84、91 题连考。

【知识关联】

  • 同库连考:与第 79 题(平均 O(n log n)=快排)对照——本题问「最坏最优」才选归并;与第 81 题(快排最坏 O(n²))、第 91 题(不退化 O(n²) 的算法)、第 86 题(归并空间 O(n))、第 83 题(归并稳定)构成归并的「复杂度-空间-稳定」三件套;与第 2 题(初态影响时间复杂度)相连:归并是少数与初态无关的算法。
  • 工程实现:Java 对象排序 TimSort 底层是归并,合同上最坏 O(n log n),专为「不能接受最坏平方」的场景;外部排序(大数据 sort、数据库 external sort)几乎都是多路归并;Git 合并、大数据 shuffle 也是归并思想。快排虽平均常数更小,但最坏要靠随机 pivot/introsort 兜底。
  • 408 / 国企真题:408 与国网「最坏情况下时间复杂度最低/最优的排序」标准答案归并(或归并与堆排);干扰项快排——学生记得「快排很快」却忽略最坏。真题变体:「在最坏情况下时间复杂度仍为 O(n log n) 的是」(归并、堆)。
  • 面试追问:①「归并三种情况?」(最好/平均/最坏都是 O(n log n));②「既然归并最坏更好,为何基本类型不用它?」(O(n) 空间,快排原地且平均更快);③「堆排最坏也是 n log n 且原地,为何对象排序不用?」(不稳定、缓存差)。

【拓展延伸】

变式问法:①「最坏复杂度最低?」(归并,本题);②「与初态无关的排序?」(归并、堆、选择);③「最坏 O(n log n) 且稳定?」(归并;堆排不稳定)。

工程映射:金融清算、审计流水排序要求结果可预期(最坏有界)且稳定,故工业上多用归并/TimSort;实时系统禁止最坏平方,就是本题结论的工程化。

易错提醒:题干「最坏情况下最优」时不能选快排;「平均最好/常数最小」才偏快排。四选项里若同时有归并和堆排而题目是单选,优先看是否强调「稳定」——本题选项只有归并,直接选 A。


81. 快速排序在最坏情况下的时间复杂度为( ),发生在( ) ​

标签:【互联网/国企笔试超高频】

A. O(n log n),数据随机时 B. O(n²),数据已有序时 C. O(n),数据全相等时 D. O(n log n),数据已有序时

答案:B

【结论】 最坏为 O(n²),发生在数据已有序时,选 B。

【逐项辨析】

  • A O(n log n),数据随机时:数据随机时快排接近平均性能 O(n log n),不是最坏。
  • B O(n²),数据已有序时:正确。 每次划分只能把问题规模减少 1。
  • C O(n),数据全相等时:题干问的是最坏情况。标准二路划分的快排在全相等数据上每趟只能划掉基准一个元素,同样退化到 O(n²);O(n) 只有改用三路划分(荷兰国旗式,把等于基准的元素一次归中)才成立,那是优化变体而非快排自身的最坏结论,故该项不选。
  • D O(n log n),数据已有序时:方向反了,已有序恰恰是最坏。

【知识点】 快排为什么在“已有序”时最坏?

以第一个元素为基准(pivot),数据已升序:
第 1 趟:划分后基准归位到最左,右半区剩 n−1 个元素
第 2 趟:右半区再划分,右半区剩 n−2 个元素
...
总比较次数 = n + (n−1) + ... + 1 = n(n+1)/2 = O(n²)
有序数组 [1, 2, 3, 4, 5],取 1 为基准:
划分后 → [1] + [2, 3, 4, 5]     每次只切掉 1 个元素
递归深度 = n,每层代价 O(n) → O(n²)

三种优化方法:

  1. 三数取中法选基准(取首、中、尾的中位数);
  2. 随机选择基准;3. 对小规模子数组切换为插入排序。

【记忆锚点】 “有序表是快排的灾星”——越有序,划分越不平衡。

【易混对比】 “数据已有序”对快排是最坏情形(O(n²)),对冒泡、插入排序却是最好情形(O(n)) —— 这一对反差是笔试最爱考的(见第 88、89 题)。

【自测】 快排取中间位置元素为基准时,数据已有序还会退化吗?

答:不会退化。 取中间元素为基准时,已有序数据的划分恰好是平衡的(左右各一半),复杂度仍为 O(n log n)。这正是“三数取中法”能解决问题的原因。

【知识关联】

  • 同库连考:与第 2 题(初态影响复杂度)、第 79 题(平均 O(n log n))、第 80/91 题(归并不退化)、第 87 题(一趟划分)、第 88/89 题(冒泡/插入「已有序=最好」)构成反差考点组——同一「数据已有序」,快排最坏、冒泡/插入最好。
  • 工程实现:JDK 双轴快排 + 随机/打乱;C++ introsort 限制递归深度并在子问题过深时切堆排;三数取中避免有序输入退化;TimSort 则干脆不用快排给对象排序。生产排序库必须处理「近有序」输入。
  • 408 / 国企真题:408「快速排序最坏情况及触发条件」标准答案 O(n²)+已有序(取首/固定 pivot 时);国网计算题会模拟第一趟划分。注意:若 pivot 取中位数,已有序不一定最坏——真题默认首元素为基准。
  • 面试追问:①「如何避免最坏?」(随机 pivot、median-of-3、小数组改插入);②「取中间元素时有序还退化吗?」(划分平衡,仍是 n log n);③「快排稳定吗?」(不稳定)。

【拓展延伸】

变式问法:①「最坏复杂度及情形?」(O(n²),数据已有序,本题);②「数据随机时?」(平均 O(n log n));③「全相等?」(朴素划分可能失衡,三路划分可 O(n) 量级处理)。

工程映射:基准测试必须含 sorted/reverse/random;分布式排序对已分区有序数据要防退化;面试手写快排常被追问有序输入。

易错提醒:题干「最坏+发生在」要同时满足复杂度与情形;D 把复杂度与情形搭配反了。「已有序」对不同算法意义不同,这是全章最经典对照。


82. 以下排序算法中,不稳定的是( ) ​

标签:【互联网/国企笔试超高频】

A. 冒泡排序 B. 归并排序 C. 快速排序 D. 插入排序

答案:C

【结论】 快速排序不稳定,选 C。

【逐项辨析】

  • A 冒泡排序:稳定(相等元素不交换)。
  • B 归并排序:稳定(合并时相等优先取左半区)。
  • C 快速排序:不稳定。 交换操作可能改变相等元素的相对顺序。
  • D 插入排序:稳定(相等时不后移)。

【知识点】 稳定性分类(必背):

稳定排序不稳定排序
冒泡排序选择排序
插入排序快速排序
归并排序希尔排序
基数排序堆排序

口诀:“选快希堆”不稳定,“归插冒基”稳定。

快排不稳定的例子:序列 [3a, 2, 3b, 1],以 3a 为基准划分
  划分后 3b 可能被交换到 3a 前面 → 相等元素相对顺序改变 → 不稳定

【记忆锚点】 “选快希堆”不稳定,“归插冒基”稳定。

【易混对比】 稳定性的实际意义:当排序关键字有多个维度时(如先按成绩排、成绩相同的保持原来的学号顺序),必须用稳定排序。例如“按成绩排序后,同分者保持原顺序”,用冒泡 / 插入 / 归并可实现,用快排 / 堆排则不能保证。

【自测】 下列哪一组全部是不稳定排序? A. 冒泡、插入、归并 B. 快排、堆排、选择 C. 归并、基数、冒泡 D. 插入、快排、堆排

答:B。快排、堆排、选择排序都不稳定。与第 83 题连考。

【知识关联】

  • 同库连考:与第 83 题(稳定的是归并)成对——「不稳定的是快排/稳定的是归并」;与第 81 题(快排最坏)、第 91/92 题(堆排不稳定、基数稳定)组成稳定性全表;与第 40 题一带的「口诀记忆」风格一致,本题口诀「选快希堆」不稳定。
  • 工程实现:Java 对象排序必须稳定 → TimSort,这是 JDK 的硬性兼容合同(程序员依赖「先按 A 再按 B,A 相同时保持 B 序」);基本类型排序不要求稳定,可用双轴快排。C++ stable_sort vs sort 对应同一分野。数据库 ORDER BY 多列时,引擎若用不稳定算法,第一列相同时第二列序可能乱——所以引擎常归并/稳定化处理。
  • 408 / 国企真题:408「下列排序算法不稳定的是」超高频;国网/银行选择题口诀化:稳定=归插冒基,不稳定=选快希堆。注意选择排序也不稳定,有时干扰项会写「选择排序稳定」。
  • 面试追问:①「快排为什么不稳定?」(划分交换可能把相等元素换到前面,如 [3a,2,3b] 以 3a 为基准);②「如何把不稳定算法变稳定?」(比较键附加原始下标作为次级键);③「多关键字排序为何要稳定?」(保证前一轮排序结果在本轮相等时被保留,即可省去多轮)。

【拓展延伸】

变式问法:①「不稳定的是?」(快排,本题;选择/希尔/堆也不稳定);②「稳定的是?」(归并、插入、冒泡、基数);③「堆排稳定吗?」(不稳定,筛选交换会打乱相等元序)。

工程映射:前端表格「先按分数排,再按姓名排」若底层不稳定会闪乱;Excel 多级排序、报表引擎都要求稳定;JDK 为对象选择 TimSort 正是把本题结论写进了 API 契约。

易错提醒:稳定性与时间/空间复杂度正交——归并稳定但要 O(n) 空间;堆排最坏 n log n 且原地但不稳定。考试把「稳定」「原地」「最坏复杂度」混在选项里是经典挖坑方式。


83. 以下排序算法中,稳定的是( ) ​

标签:【互联网/国企笔试超高频】

A. 快速排序 B. 堆排序 C. 归并排序 D. 选择排序

答案:C

【结论】 归并排序稳定,选 C。

【逐项辨析】

  • A 快速排序:不稳定。
  • B 堆排序:不稳定(堆调整会打乱相等元素的顺序)。
  • C 归并排序:稳定。 合并两个有序子序列时,遇相等元素优先取前一个子序列的元素,从而保持稳定性。
  • D 选择排序:不稳定(交换可能跨越相等元素)。

【知识点】 归并排序为什么稳定?

合并两个有序子序列时,比较规则:
  若 left[i] <= right[j] → 取 left[i]     ← 注意用"<="
  否则                    → 取 right[j]

用"<="意味着相等时优先取左半区的元素,
而左半区的元素原本就在前面 → 相对顺序保持不变 → 稳定 ✓
排序算法稳定性稳定的原因
冒泡排序✅只有严格逆序时才交换
插入排序✅相等时不再后移
归并排序✅合并时相等优先取左
基数排序✅按位分配收集,天然保持顺序
快速排序❌交换跨越相等元素
选择排序❌交换跨越相等元素
希尔排序❌分组插入破坏相对顺序
堆排序❌堆调整打乱顺序

【记忆锚点】 “归插冒基”稳定(归并、插入、冒泡、基数)。

【易混对比】 注意“稳定”与“原地”是两个独立维度:归并排序稳定但不原地(需 O(n) 额外空间);堆排序原地但不稳定。考试中常把这两个维度交叉设置选项。

【自测】 归并排序的稳定性靠什么保证?

答:靠合并时相等元素优先取左半区的规则(用 left[i] <= right[j] 才取左)。与第 82 题连考。

【知识关联】

  • 同库连考:与第 82 题(不稳定的是快排)成对互锁;与第 79/80/91 题(复杂度)、第 86 题(归并空间 O(n))、第 92 题(基数排序也稳定)同属排序性质网;与第 16 题(两个有序表合并)是归并的母操作,稳定性就在合并比较规则里产生。
  • 工程实现:Java Arrays.sort(Object[]) 强制稳定(TimSort);Python、Ruby 对象排序同样稳定;JDK 文档直接写明 stable sort,因为大量业务代码依赖「先按时间再按金额」这类多键排序的隐含顺序。外部排序、数据库 sort 也优先稳定归并。
  • 408 / 国企真题:408「下列排序算法稳定的是」与第 82 题互为镜像;国网题库要求默写「归插冒基稳定,选快希堆不稳定」。常考变体:给序列演示归并合并过程,问相等元素最终相对位置。
  • 面试追问:①「归并为何稳定?」(合并时 left[i] <= right[j] 优先取左,相等元保持原前后);②「若改成 < 会怎样?」(相等时优先取右,稳定性被破坏);③「稳定 vs 原地?」(归并稳定不原地;堆排原地不稳定;两者维度不同)。

【拓展延伸】

变式问法:①「稳定的是?」(归并,本题);②「基数排序稳定吗?」(稳定,按位分配收集保持顺序);③「希尔排序稳定吗?」(不稳定,跨步插入打乱)。

工程映射:报表「一级部门、二级部门」多级排序;消息队列按 partition 内 offset 有序;电商「销量优先,同销量保持上架顺序」——业务语义常隐含稳定排序假设,算法选型必须兑现。

易错提醒:稳定性的保证写在合并/交换规则里,不是算法名字玄学;背口诀之外要能解释归并合并的 <=。另:存在「稳定堆排序」的研究实现,但经典堆排序按教材口径判不稳定。


84. 堆排序的时间复杂度在最好、平均、最坏情况下分别为( ) ​

标签:【互联网/国企笔试高频】

A. O(n), O(n log n), O(n²) B. O(n log n), O(n²), O(n²) C. O(n), O(n), O(n log n) D. O(n log n), O(n log n), O(n log n)

答案:D

【结论】 最好、平均、最坏均为 O(n log n),选 D。

【逐项辨析】

  • A O(n), O(n log n), O(n²):混入了快排的最坏情况。
  • D O(n log n), O(n log n), O(n log n):正确。 三种情况一致。
  • C O(n), O(n), O(n log n):误把建堆的 O(n) 当成整体复杂度。
  • B O(n log n), O(n²), O(n²):混入了快排的复杂度。

【知识点】 堆排序的复杂度分解

建初始堆:        O(n)            ← 自底向上调整,线性时间(见第 85 题)
反复取堆顶并调整堆:O(log n) × n 次 = O(n log n)
总复杂度:        O(n log n)      ← 建堆的 O(n) 被 O(n log n) 吸收
排序算法最好平均最坏空间稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)❌
归并排序O(n log n)O(n log n)O(n log n)O(n)✅
快速排序O(n log n)O(n log n)O(n²)O(log n)❌

堆排序的两个突出优点:

  1. 时间复杂度稳定 —— 不受数据初始状态影响;2. 空间 O(1) —— 原地排序(优于归并的 O(n))。

【记忆锚点】 “堆排序:时间稳(都是 n log n)、空间省(原地 O(1)),可惜不稳定”。

【易混对比】 与归并排序(第 80 题)同为“三种情况都是 O(n log n)”,区别在

堆排序归并排序
空间O(1)O(n)
稳定性❌✅
数据访问跳跃(缓存不友好)顺序(缓存友好)

【自测】 在空间受限的场景下,堆排序和归并排序哪个更优?

答:堆排序。堆排序空间 O(1),归并排序需 O(n) 额外空间。与第 86 题连考。

【知识关联】

  • 同库连考:与第 85 题(堆排复杂度)、第 93 题(TopK)、第 75 题(下标)。
  • 工程实现:Java PriorityQueue 小顶堆。
  • 408 / 国企真题:408 与国网。
  • 面试追问:①「建堆为何 O(n)?」(自底向上下沉总和收敛);②「逐个插入建堆?」(O(n log n))。

【拓展延伸】

完整验算:高度 h 层约 n/2^{h+1} 个结点 × 代价 h → Σ 收敛 O(n)。

变式:d 叉堆。

工程应用:定时器、任务优先级、TopK。


85. 对n个元素进行堆排序时,建初始堆的时间复杂度为( ) ​

标签:【互联网/国企笔试高频】

A. O(n log n) B. O(n) C. O(n²) D. O(log n)

答案:B

【结论】 建初始堆的时间复杂度为 O(n),选 B。

【逐项辨析】

  • A O(n log n):这是“排序过程”的复杂度,不是建堆。
  • B O(n):正确。 自底向上建堆的总代价是线性的。
  • C O(n²):无依据。
  • D O(log n):这是单次下沉调整的复杂度。

【知识点】 建堆为什么是 O(n) 而不是 O(n log n)?

自底向上建堆:从最后一个非叶结点开始,依次向前做"下沉"调整

关键:不同深度的结点,下沉代价不同
  深度 1 的结点(约 n/2 个):下沉 0 次
  深度 2 的结点(约 n/4 个):下沉最多 1 次
  ...
  根结点(1 个):            下沉最多 log n 次

总代价 = Σ(该层结点数 × 该层下沉次数)
       = n/4×1 + n/8×2 + n/16×3 + ... + 1×log n
       < n × (1/4 + 2/8 + 3/16 + ...) < n × 1 = O(n)

直观理解:绝大多数结点都在底层,下沉次数很少;只有极少数结点在顶层,下沉次数多。加权求和后收敛到线性。

自底向上建堆示意(数组 [4, 1, 3, 2, 16, 9, 10],n = 7,**以下标 1 起计**:a[1]=4…a[7]=10):
最后一个非叶结点 = 下标 ⌊7/2⌋ = 3(a[3]=3)→ 下沉
下标 2(a[2]=1)→ 下沉
下标 1(值 4)→ 下沉(根结点,下沉次数最多)

【记忆锚点】 “建堆 O(n),排序 O(n log n)”——建堆是“一次性”的线性工作,排序是“n 次取顶”的对数工作。

【易错提醒】 这是一个精确的线性时间复杂度,而非 O(n log n) 的粗略估计 —— 很多人误答 O(n log n)。注意区分:建堆 O(n),而整个堆排序过程(反复取堆顶并调整)是 O(n log n)。

【易混对比】 注意“逐个插入建堆”与“自底向上建堆”的区别:逐个插入建堆的复杂度是 O(n log n);自底向上建堆才是 O(n) —— 教材与考试通常指的是后者。

【自测】 堆排序的建堆是 O(n),那整个堆排序的总复杂度是多少?

答:O(n log n)。建堆 O(n) 加上 n 次“取堆顶 + 调整”的 O(n log n)。与第 84 题连考。

【知识关联】

  • 同库连考:与第 84 题建堆 O(n) 区分 —— 建堆线性,整个堆排 n log n;与第 83 题不稳定。
  • 工程实现:同堆。
  • 408 / 国企真题:必考。
  • 面试追问:①「空间?」(O(1) 原地);②「缓存?」(比快排差)。

【拓展延伸】

完整验算:n 次取顶 × log n 调整。

变式:奇偶堆排序并行。

工程应用:内存受限场景。


86. 以下关于排序算法空间复杂度的说法,正确的是( ) ​

标签:【互联网/国企笔试高频】

A. 快速排序的空间复杂度为O(1) B. 冒泡排序的空间复杂度为O(n) C. 堆排序的空间复杂度为O(n) D. 归并排序的空间复杂度为O(n)

答案:D

【结论】 归并排序空间复杂度为 O(n),该说法正确,选 D。

【逐项辨析】

  • A错:快速排序空间复杂度为 O(log n)(递归栈深度),不是 O(1)。
  • D 对:归并排序需要 O(n) 的额外空间来合并子序列。
  • C错:堆排序空间复杂度为 O(1)(原地排序)。
  • B错:冒泡排序空间复杂度为 O(1)(只需一个临时变量)。

【知识点】 各排序算法的空间复杂度(必背):

排序算法空间复杂度是否原地
冒泡排序O(1)✅
插入排序O(1)✅
选择排序O(1)✅
希尔排序O(1)✅
堆排序O(1)✅
快速排序O(log n)基本原地(递归栈)
归并排序O(n)❌
基数排序O(n + r)❌
为什么快排是 O(log n) 而非 O(1)?
快排是递归实现的,递归调用栈的深度 = 递归树高度
平均情况递归深度为 O(log n) → 栈空间 O(log n)
(最坏情况下递归深度为 O(n),栈空间 O(n))

【记忆锚点】 “归并最费空间(O(n)),快排花在栈上(O(log n)),其余都是 O(1)”。

【易混对比】 空间复杂度与稳定性是两个独立维度,常被交叉考查

算法空间稳定
堆排序O(1)❌
归并排序O(n)✅
快速排序O(log n)❌

在空间有限的场景下,堆排序优于归并排序。

【自测】 若要求“空间 O(1) 且时间复杂度稳定为 O(n log n)”,应该选哪个排序?

答:堆排序。空间 O(1),三种情况都是 O(n log n)。与第 84 题连考。

【知识关联】

  • 同库连考:与第 79/80/91 题(归并时间复杂度)、第 82/83 题(稳定性)、第 2 题(复杂度口径);快排递归栈空间平均 O(log n)、最坏 O(n) 见第 86 题组成排序「时间-空间-稳定」三维表;与第 92 题(基数 O(n+r) 空间)对照。
  • 工程实现:归并需辅助数组 O(n),外部排序用多路归并控制内存缓冲;快排递归栈平均 O(log n)、最坏可到 O(n);堆排原地 O(1) 常用于嵌入式/内存受限。JDK 对象 TimSort 空间也是 O(n) 量级。
  • 408 / 国企真题:408「归并排序空间复杂度」O(n);「下列排序空间 O(1) 的是」考堆/冒泡/插入/选择。国网喜欢把空间与稳定性交叉设选项。
  • 面试追问:①「快排空间为何不是 O(1)?」(递归栈);②「如何把归并做成 O(1) 额外空间?」(原地归并技巧复杂,实用少;链表归并改指针可 O(1));③「既要 O(1) 空间又要最坏 n log n?」(堆排序)。

【拓展延伸】

变式问法:①「归并空间?」(O(n),本题);②「快排平均空间?」(O(log n) 栈);③「冒泡/堆排空间?」(都是 O(1))。

工程映射:大数据 sort 磁盘多路归并、流式归并合并有序日志、嵌入式选堆排、JVM 栈深度限制与快排最坏。

易错提醒:「原地」≠ 空间严格 O(1)(快排有栈);归并「稳定但费空间」是经典权衡。选项 C 堆排序空间 O(1),不要误选。


87. 设一组初始记录关键字序列为 (45, 80, 55, 40, 42, 85),以第一个记录关键字 45 为基准,按严蔚敏教材的挖坑填数法作一趟快速排序(升序),划分结果为( ) ​

标签:【国企笔试超高频】

A. 42, 40, 45, 80, 85, 55 B. 40, 42, 45, 55, 80, 85 C. 42, 40, 45, 55, 80, 85 D. 42, 40, 45, 85, 55, 80

答案:C

【知识点】 「一趟划分」的不变量与实现差异:一趟划分只保证三件事:

  1. 基准元素到达最终有序位置;2. 左侧全部 ≤(或 <)基准;3. 右侧全部 ≥(或 >)基准。 不保证左右子段内部有序。
实现特点本题
挖坑填数法(严蔚敏)交替填坑答案 C
Lomuto从左扫小的换到前部中间序列可能不同
Hoare双指针交换中间序列可能不同

「基准归位 + 左小右大」只是必要条件,四个选项都满足它,因此真正决定答案的是题干指定的划分方法:挖坑法保持右侧相对次序为 55, 80, 85,得 C;而双指针交换法(Hoare 型)在同一输入上会得 B。验算方法:数基准下标,检查左右集合,再按指定算法逐步移动。

【分步模拟 · 挖坑填数法】 初始 [45, 80, 55, 40, 42, 85],pivot = 45

步骤动作数组状态坑位
0挖出 pivot = 45[__ , 80, 55, 40, 42, 85]0
1high 左移找 < 45:命中 42[42, 80, 55, 40, __ , 85]4
2low 右移找 > 45:命中 80[42, __ , 55, 40, 80, 85]1
3high 左移找 < 45:命中 40[42, 40, 55, __ , 80, 85]3
4low 右移找 > 45:命中 55[42, 40, __ , 55, 80, 85]2
5low 与 high 相遇于 2,填入 45[42, 40, 45, 55, 80, 85]—

【逐项辨析】 逐一对照挖坑法的实际移动结果(右侧相对次序应为 55, 80, 85):

  • A (42, 40, 45, 80, 85, 55):右侧变成 80, 85, 55,与挖坑法的移动结果不符(high 侧是把 55 留在原地、80/85 依次后移,而非把 55 甩到末位)。
  • B (40, 42, 45, 55, 80, 85):这是双指针交换法在同一输入上作一趟划分的结果(实测:先 80↔42、再 55↔40,最后基准与 j 位互换 → 40,42,45,55,80,85)。它满足"基准归位 + 左小右大",所以若题干不限定方法,本题就有 B、C 两个正确答案——题干已按挖坑法限定,故排除 B。注意:它恰好也是完全有序序列,但"看起来有序"不是排除它的理由。
  • C (42, 40, 45, 55, 80, 85):正确。 与上方逐步模拟一致。
  • D (42, 40, 45, 85, 55, 80):右侧 85, 55, 80 与挖坑法的移动结果不符。

【知识点】 “一趟划分”只保证三件事

① 基准元素归位(45 到了它的最终位置)
② 左侧所有元素 < 基准({42, 40} < 45)
③ 右侧所有元素 > 基准({55, 80, 85} > 45)

左右两侧内部是否有序,一趟划分不管!

【易错提醒】 ①「一趟划分」只保证基准归位、左小右大,不保证左右两侧内部有序——看到「已完全有序」的选项不要条件反射地排除:本题的 B(40,42,45,55,80,85)恰好同时是双指针交换法一趟划分的结果;②正因如此,这类题的答案由题干指定的划分方法决定:挖坑填数法得 C、交换法得 B,所以严蔚敏系教材的卷面要按挖坑法做,题干若没写明方法就是命题不严谨;③验算方法:数基准的下标,再检查左右两个集合。国网真题库中此类“求一趟划分结果”的题目出现频率极高。

【记忆锚点】 “一趟划分只管基准归位、左小右大;左右内部乱不乱,不关它的事”。

【易混对比】 两种划分实现的差异

实现教材特点
挖坑填数法严蔚敏《数据结构》交替填坑,本题答案
交换法(左右交换)部分教材 / Lomuto 变体结果可能不同

但判据不变:基准归位 + 左小右大。 答题时按你所用教材的实现模拟;若拿不准,优先选“基准归位且左右分组正确”的那个。

【自测】 序列 (49, 38, 65, 97, 76, 13, 27, 49),以第一个元素 49 为基准,按挖坑填数法做一趟划分,结果是?

答:(27, 38, 13, 49, 76, 97, 65, 49)。这是严蔚敏教材的经典例题,国网与 408 均考过。

【知识关联】

  • 同库连考:与第 81 题(最坏 O(n²) 的成因)、第 79 题(平均 O(n log n))构成快排三连;国网真题库「一趟划分」高频。
  • 工程实现:Lomuto/Hoare/挖坑三种划分中间序列不同,但「基准归位+左小右大」不变。
  • 408 / 国企真题:国网超高频;408 也考。
  • 面试追问:①「Hoare 与挖坑结果一定相同吗?」(不一定);②「一趟后基准下标?」(最终正确位置)。

【拓展延伸】

完整验算(挖坑法,已在原解析逐步给出):

[45,80,55,40,42,85] pivot=45
最终 [42,40,45,55,80,85]
验:左{42,40}<45,右{55,80,85}>45,基准下标2 ✓
不是完全有序(左右内部未排)

变式:以中间元素为基准;三数取中;双轴快排一趟。

工程应用:quickselect 求第 k 大基于同一划分。


88. 冒泡排序在最好情况下的时间复杂度为( ) ​

标签:【互联网/国企笔试高频】

A. O(n) B. O(n log n) C. O(n²) D. O(log n)

答案:A

【结论】 最好情况下时间复杂度为 O(n),选 A。

【逐项辨析】

  • A O(n):正确。 数据已有序 → 第一趟没有任何交换 → 标志位判定“未发生交换” → 直接结束。只做了 n−1 次比较、0 次移动。
  • B O(n log n):这是快排 / 归并 / 堆排的平均复杂度,与冒泡无关。
  • C O(n²):这是冒泡最坏(逆序)与平均情况,不是“最好”。
  • D O(log n):排序至少要读一遍全部数据,下界是 O(n),不可能到对数级。

【知识点】 冒泡排序三种情况

情况比较次数移动次数复杂度
最好(已有序 + 标志位)n − 10O(n)
最坏(完全逆序)n(n−1)/2n(n−1)/2 × 3O(n²)
平均≈ n²/2≈ n²/4O(n²)

「移动次数」两行的口径不同(比较前先统一单位):最坏行按一次交换 = 3 次赋值计,故为 3 × n(n−1)/2;平均行的 ≈ n²/4 按交换次数计——冒泡只交换相邻逆序对,交换总次数恰等于逆序对数,随机排列的期望逆序对数为 n(n−1)/4(n=2…8 全排列实跑验证:0.5/1.5/3/5/7.5/10.5/14,与 n(n−1)/4 完全一致)。若把平均行也换算成赋值次数,应为 3n(n−1)/4 ≈ 0.75n²。

关键前提:“最好 O(n)”成立的条件是带标志位优化。若不加标志位,冒泡无论如何都要跑满 n−1 趟,最好情况也是 O(n²)。

c
// 带标志位的冒泡排序
for (i = 0; i < n - 1; i++) {
    flag = 0;                          // 本趟是否发生交换
    for (j = 0; j < n - 1 - i; j++)
        if (a[j] > a[j+1]) {
            swap(a[j], a[j+1]);
            flag = 1;
        }
    if (flag == 0) break;              // 无交换 → 已有序 → 提前结束
}

【记忆锚点】 “有序表是冒泡的福星、快排的灾星”——冒泡遇到有序表最好 O(n),快排遇到有序表(且取首元素为基准)最坏退化成 O(n²)。这一对反差是笔试最爱考的。

【易混对比】 常见排序算法的复杂度与稳定性(必背):

算法最好平均最坏稳定性
直接插入排序O(n)O(n²)O(n²)✅ 稳定
冒泡排序O(n)O(n²)O(n²)✅ 稳定
简单选择排序O(n²)O(n²)O(n²)❌ 不稳定
快速排序O(n log n)O(n log n)O(n²)❌ 不稳定
堆排序O(n log n)O(n log n)O(n log n)❌ 不稳定
归并排序O(n log n)O(n log n)O(n log n)✅ 稳定

【自测】 下列排序算法中,最好情况时间复杂度为 O(n) 的是( ) A. 简单选择排序 B. 堆排序 C. 直接插入排序 D. 快速排序

答:C。直接插入排序在数据已有序时只需 n−1 次比较、0 次移动。与本题对照记忆。

【知识关联】

  • 同库连考:与第 89 题(插入最好 O(n))同型;与第 81 题(快排最坏有序)反差。
  • 工程实现:带 flag 的冒泡在已有序时 O(n);工程几乎不用冒泡。
  • 408 / 国企真题:必考。
  • 面试追问:①「无 flag 冒泡最好?」(仍 O(n²) 因固定趟);②「鸡尾酒排序?」(双向冒泡)。

【拓展延伸】

完整验算:flag 版一趟无交换即停。

变式:插入排序最好也是 O(n)。

工程应用:教学与小数据。


89. 插入排序在最好情况下的时间复杂度为( ) ​

标签:【互联网/国企笔试高频】

A. O(n) B. O(n log n) C. O(n²) D. O(log n)

答案:A

【结论】 最好情况下时间复杂度为 O(n),选 A。

【逐项辨析】

  • A O(n):正确。 数据已有序 → 每个元素只需与前一个比较一次,不需移动。
  • B O(n log n):插入排序达不到这个量级。
  • C O(n²):这是插入排序的最坏(逆序)与平均情况。
  • D O(log n):排序下界是 O(n)。

【知识点】 插入排序三种情况

情况比较次数移动次数复杂度
最好(已有序)n − 10O(n)
最坏(完全逆序)n(n−1)/2n(n−1)/2O(n²)
平均≈ n²/4≈ n²/4O(n²)
c
// 直接插入排序
for (i = 1; i < n; i++) {
    tmp = a[i];
    for (j = i - 1; j >= 0 && a[j] > tmp; j--)
        a[j+1] = a[j];              // 元素后移
    a[j+1] = tmp;                    // 插入到正确位置
}

“数据基本有序”时插入排序效率非常高 —— 这正是希尔排序把插入排序作为子过程的原因(见第 90 题)。

【记忆锚点】 “插入排序越有序越快”——已有序时退化为“只比较不移动”。

【易混对比】 与第 88 题(冒泡最好 O(n))同型。最好情况能达到 O(n) 的比较排序:直接插入排序、带标志位的冒泡排序 —— 二者都利用了“数据已有序”这一条件。

【自测】 下列哪种排序在数据“基本有序”时效率最高? A. 快速排序 B. 堆排序 C. 直接插入排序 D. 简单选择排序

答:C。插入排序在基本有序时接近 O(n)。与第 90 题连考。

【知识关联】

  • 同库连考:与第 88 题(冒泡最好 O(n))同属「最好情况 O(n)」家族;与第 81 题(快排已有序却是最坏 O(n²))形成反直觉对照组,是排序章最爱连考的一对;与第 90 题(希尔排序以插入为子过程)、第 79/80 题(复杂度表)衔接。
  • 工程实现:JDK TimSort / C++ introsort 在小规模 run(如 ≤32) 上切换到插入排序,因为「基本有序的短序列」上插入接近 O(n) 且常数极小;归并的合并段若一侧已耗尽,另一侧直接拷贝,也吃到了「有序」红利。工程混合排序的本质就是「分情况用最合适的算法」。
  • 408 / 国企真题:408「直接插入排序最好时间复杂度」答案 O(n);国网计算题常问「已有序时比较 n−1 次、移动 0 次」。干扰项 O(n log n) 来自误套排序下界——下界约束的是最坏/平均的比较排序全体,不否定特定算法在特定初态下的 O(n)。
  • 面试追问:①「数据基本有序时用什么排序?」(插入排序,接近 O(n));②「二分插入排序?」(比较次数降为 O(n log n),移动仍 O(n²),总复杂度不变);③「为什么有序时快排反而慢?」(首元/固定基准划分极度不平衡)。

【拓展延伸】

变式问法:①「插入排序最好?」(O(n),本题);②「最坏/平均?」(都是 O(n²));③「冒泡最好为何也是 O(n)?」(带标志位,一趟无交换即停)。

工程映射:自适应排序(adaptive sort)检测「近有序」输入;数据库对已索引小结果集做插入式重排;游戏存档增量排序、日志按时间戳追加后的最后一公里排序,都适合插入。

易错提醒:「最好 O(n)」≠「平均也快」;也不能据此说插入排序比归并「更好」。考点是初态敏感性:同一算法不同初态差一个数量级,呼应第 2 题总纲。


90. 希尔排序是( )的改进版本。 ​

标签:【互联网/国企笔试高频】

A. 冒泡排序 B. 选择排序 C. 插入排序 D. 归并排序

答案:C

【结论】 希尔排序是插入排序的改进版本,选 C。

【逐项辨析】

  • A 冒泡排序:希尔排序与冒泡无关。
  • B 选择排序:希尔排序不是选择排序的改进。
  • C 插入排序:正确。 希尔排序又称“缩小增量排序”,本质是分组插入排序。
  • D 归并排序:希尔排序不涉及分治合并。

【知识点】 希尔排序的原理

1. 取一个增量 gap(如 n/2),把数组按下标间隔 gap 分成若干组
2. 对每组分别做插入排序
3. 缩小 gap(如 gap = gap/2),重复上述过程
4. 直到 gap = 1,做最后一次完整的插入排序
示例:[49, 38, 65, 97, 76, 13, 27, 49],n = 8

第 1 趟 gap = 4:
  分组:(49,76) (38,13) (65,27) (97,49)
  组内插入排序后:[49, 13, 27, 49, 76, 38, 65, 97]

第 2 趟 gap = 2:
  分组:(49,27,76,65) (13,49,38,97)
  组内插入排序后:[27, 13, 49, 38, 65, 49, 76, 97]

第 3 趟 gap = 1:
  完整插入排序 → 最终有序

核心思想:先通过大增量让元素“大步跳跃”接近目标位置,使数组基本有序;最后 gap = 1 时的插入排序代价很低。

【记忆锚点】 “希尔排序:先分组插排,增量逐渐减半,最后一趟归位”。

【易错提醒】 ①希尔排序的时间复杂度取决于增量序列,通常为 O(n^1.3) 左右(取 n/2 序列时约为 O(n^1.5));②希尔排序不稳定 —— 分组插入会打乱相等元素的相对顺序;③增量序列的最后一个值必须是 1,否则不能保证最终有序。

【易混对比】 希尔排序与插入排序的关系

插入排序希尔排序
增量固定为 1由大到小(最后为 1)
平均复杂度O(n²)约 O(n^1.3)
稳定性✅❌
适用数据量小 / 基本有序数据量中等

【自测】 希尔排序的增量序列最后一项必须是多少?

答:1。只有 gap = 1 时做完整插入排序,才能保证整个数组有序。与第 89 题连考。

【知识关联】

  • 同库连考:与第 89 题(直接插入最好 O(n))是「改进前 vs 改进后」;与第 91 题(不退化的排序)、第 83 题(稳定性)、第 85 题(建堆 O(n))同属排序选型族;与附录对比表连看。
  • 工程实现:增量序列决定性能, Hibborn(Sedgewick) 增量经验上优于 n/2 折半;间隔子序列各自插入、最后 gap=1 收口。
  • 408 / 国企真题:408 常考「下列排序中不稳定/时间复杂度与初态无关」的集合判断;国网考「最后一趟增量必须为 1」。
  • 面试追问:①「希尔为什么不稳定?」(跨间隔交换会打乱等值元素相对次序);②「和快排的取舍?」(希尔实现简单、无递归栈,但最坏仍可到 O(n²) 量级,视增量序列)。

【拓展延伸】

完整验算:以 8 个元素、增量序列 4→2→1 逐步跑一遍,gap=4 比较 4 次、gap=2 与 gap=1 各自完成本组插入,最终有序 —— 见本题【分步推导】。

变式:①「增量序列取 3,2,1 行不行?」(可以,但每轮必须是降序且最后为 1);②「希尔排序能否做到 O(n log n)?」(取决于增量序列,Shell 原始 n/2 序列最坏 Θ(n²),Sedgewick/Knuth 序列更好)。

工程应用:嵌入式或对代码体积敏感的场合用插入/希尔;大数据量不用希尔(缓存与最坏不友好),改用快排/归并。


91. 以下哪种排序算法在任何情况下时间复杂度都不会退化为O(n²)? ​

标签:【互联网/国企笔试高频】

A. 快速排序 B. 冒泡排序 C. 归并排序 D. 插入排序

答案:C

【结论】 归并排序在任何情况下都不退化为 O(n²),选 C。

【逐项辨析】

  • A 快速排序:最坏 O(n²)(数据已有序时)。
  • B 冒泡排序:最坏 O(n²)(完全逆序时)。
  • C 归并排序:正确。 无论数据初始状态如何,始终为 O(n log n)。
  • D 插入排序:最坏 O(n²)(完全逆序时)。

【知识点】 归并排序为什么“不受数据初始状态影响”?

归并排序的结构:
  把数组分成两半 → 递归排序左半 → 递归排序右半 → 合并

分治结构与数据内容无关:
  无论数据是否有序,总是分到单个元素再逐层合并
  递归树高度恒为 ⌈log₂n⌉,每层合并代价恒为 O(n)
  → 总复杂度恒为 O(n log n)
排序算法最坏复杂度是否受数据初态影响
快速排序O(n²)✅ 受影响(有序 → 最坏)
冒泡排序O(n²)✅ 受影响(逆序 → 最坏)
插入排序O(n²)✅ 受影响(逆序 → 最坏)
选择排序O(n²)❌ 不受影响(但恒为 O(n²))
归并排序O(n log n)❌ 不受影响
堆排序O(n log n)❌ 不受影响

【记忆锚点】 “归并、堆排序:三种情况一个数(n log n);快排、冒泡、插入:怕有序 / 逆序”。

【易混对比】 注意本题问“任何情况下都不退化为 O(n²)” —— 堆排序也满足!所以严格说 C 正确(题目为单选,归并是标准答案),但堆排序同样是“最坏 O(n log n)”。两者的差别在稳定性(归并稳定,堆排不稳定)和空间(归并 O(n),堆排 O(1))。

【自测】 哪些排序算法的最坏情况时间复杂度为 O(n log n)?

答:归并排序、堆排序(快排最坏 O(n²))。与第 80、84 题连考。

【知识关联】

  • 同库连考:与第 80 题(最坏最优=归并)、第 79 题(平均 n log n=快排)、第 81 题(快排最坏 n²)、第 84 题(堆排)、第 86 题(归并空间 O(n))构成排序性质矩阵;与第 2 题(初态影响)相连:归并/堆排/选择是与初态无关一族。
  • 工程实现:实时/交易系统禁止最坏平方,选用归并或堆排;introsort=快排+深度监控+堆排兜底,保证最坏 O(n log n);TimSort 最坏 n log n。「不退化」是高可靠系统的排序合同。
  • 408 / 国企真题:408「下列哪种排序在任何情况下时间复杂度不会退化为 O(n²)」标准答案归并(堆排若出现也是对的,本题单选选 C);常见错选快排(只记得平均快)。
  • 面试追问:①「归并为何不退化?」(分治结构与数据内容无关,递归树高恒 ⌈log₂n⌉);②「堆排也不退化,与归并差别?」(归并稳定但 O(n) 空间;堆排不稳定但 O(1) 空间);③「选择排序初态影响吗?」(不影响比较次数结构,但恒 O(n²),不是本题答案)。

【拓展延伸】

变式问法:①「不退化 O(n²) 的是?」(归并,本题;堆排同满足);②「最坏 O(n log n) 且稳定?」(归并);③「最坏 O(n log n) 且原地?」(堆排)。

工程映射:SLA 明确「排序延迟上界」的系统;库函数混合排序的兜底层;对抗性输入(故意构造最坏数据)的安全场景。

易错提醒:「任何情况下都不退化」比「平均很快」要求更高;快排有最坏路径。若多选题,归并与堆排都选;单选按题库标准选归并。


92. 基数排序的时间复杂度为( ),其中n为元素个数,d为关键字位数,r为基数。 ​

标签:【互联网/国企笔试高频】

A. O(n log n) B. O(n) C. O(n²) D. O(d × (n + r))

答案:D

【结论】 时间复杂度为 O(d × (n + r)),选 D。

【逐项辨析】

  • A O(n log n):这是比较排序的下界,基数排序不比较。
  • D O(d × (n + r)):正确。 d 趟,每趟 O(n + r)。
  • C O(n²):无依据。
  • B O(n):只有在 d 为常数且 r = O(n) 时才退化为线性。

【知识点】 基数排序的复杂度分析

基数排序(以最低位优先 LSD 为例):
  对每个关键字位做一趟"分配 + 收集"
  共 d 趟(d = 关键字的最大位数)
  每趟:分配 O(n) + 收集 O(r)
  总复杂度 = d × (n + r)

其中:n = 元素个数,d = 关键字位数,r = 基数(如十进制 r = 10)
示例:对 [329, 457, 657, 839, 436, 720, 355] 排序
第 1 趟(个位):按个位分配到 0~9 号桶 → 收集
第 2 趟(十位):按十位分配到 0~9 号桶 → 收集
第 3 趟(百位):按百位分配到 0~9 号桶 → 收集 → 有序
共 d = 3 趟,r = 10

当 d 为常数、r = O(n) 时,复杂度退化为 O(n) —— 这是非比较排序能突破 O(n log n) 下界的原因。

【记忆锚点】 “d 趟 × 每趟(n + r)”——趟数乘上“分配 n 个 + 收集 r 个”。

【易混对比】 基数排序的关键性质

性质值说明
时间复杂度O(d(n+r))d 为常数时可视为 O(n)
空间复杂度O(n + r)需要 r 个桶
稳定性✅ 稳定按位分配收集天然保持顺序
是否比较❌ 不比较属于非比较排序

【自测】 基数排序的空间复杂度是多少?

答:O(n + r)。需要 n 个元素的暂存空间和 r 个桶。与第 94 题连考。

【知识关联】

  • 同库连考:与第 83 题(基数排序稳定)、第 79 题(比较排序下界 O(n log n))对照——基数是非比较排序,可突破下界;与第 94 题(不需要进行元素比较的排序)同属非比较排序族;与第 2/3 题复杂度概念衔接:O(d(n+r)) 在 d、r 为常数时才是线性量级。
  • 工程实现:Java 没有直接暴露的基数排序 API,但计数排序/基数排序用于 Android 某些路径、大数据整数排序库(如部分 radix sort 实现);PostgreSQL 对定长短键、内存排序引擎在 d 很小时可选桶/基数思想;JSON/字符串按字典序分位处理也是基数展开。工程上「整数、位数少、范围可控」才用基数。
  • 408 / 国企真题:408「基数排序时间复杂度」标准 O(d(n+r));国网/银行常设「与 O(n log n) 比较」判断题——当 d 为常数且 r=O(n) 时可到 O(n),故非比较排序不受比较下界限制,这是必背理论点。
  • 面试追问:①「基数排序为何能突破 n log n?」(不基于关键字比较,而按位分配);②「d 很大时还快吗?」(d 若随关键字长度线性增长,复杂度优势消失);③「空间复杂度?」(O(n+r),桶+暂存);④「LSD 与 MSD 区别?」(最低位优先稳定逐位;最高位优先可分治但实现更复杂)。

【拓展延伸】

变式问法:①「基数排序复杂度?」(O(d(n+r)),本题);②「十进制整数 d 位、r=10 时?」(约 O(d·n));③「何时退化为平方?」(题设若强行解释,通常不选 O(n²);除非错误实现)。

工程映射:限流计数器按位排序、IP 地址分段、固定格式工号/卡号排序、多关键字「先按年再按月再按日」可用三次计数/基数完成,且天然稳定。

易错提醒:选项 B「O(n)」只在 d 常数且 r 规模适中时才「可视为」线性,严格按定义选 D;A 是比较排序下界,基数不属于比较排序,不要套用。


93. 数据流中不断有元素到达,需要实时维护当前最大的 K 个数(TopK),最优的数据结构是( ) ​

标签:【互联网面试高频】

A. 数组,每次插入后重新排序 B. 大小为 K 的堆 C. 二叉排序树 D. 哈希表

答案:B【结论】 数据流 TopK 最优结构是大小为 K 的堆,选 B。

【逐项辨析】

  • A 数组,每次插入后重新排序:每次操作 O(n log n),数据量大时不可接受。
  • B 大小为 K 的堆:正确。 每次操作 O(log K),可实时维护 TopK。
  • C 二叉排序树:维护前 K 大不如堆直接,且需额外处理淘汰。
  • D 哈希表:哈希表不维护顺序,无法得到 TopK。

【知识点】 数据流 TopK 的解法

维护一个大小为 K 的小顶堆(堆顶是当前第 K 大)
新元素到达时:
  若堆未满    → 入堆
  若 x > 堆顶 → 弹出堆顶,x 入堆
  否则        → 丢弃
每次操作 O(log K),查询 TopK 时堆内即为答案
方案单次操作是否适合数据流
每次重新排序O(n log n)❌ 太慢
大小为 K 的堆O(log K)✅ 最优
平衡二叉搜索树O(log n)可行但更重

【记忆锚点】 “数据流不停来,小顶堆实时守门”——门槛是堆顶,比它大就换人。

【易混对比】 与第 47 题(静态数据 TopK)同型

静态数据数据流
特点数据一次性给定元素不断到达
方案整体建堆 / 快速选择必须增量维护堆
复杂度快速选择平均 O(n)每次 O(log K)

“海量数据 TopK / 数据流 TopK”是互联网笔试最高频考点之一。

【自测】 数据流 TopK 为什么用小顶堆而不是大顶堆?

答:因为小顶堆的堆顶是“当前 K 个中的最小值”,也就是门槛;新元素比门槛大才有资格进入。大顶堆的堆顶是最大值,无法做淘汰判断。与第 47 题连考。

【知识关联】

  • 同库连考:与第 47 题(静态数据 TopK)、第 14 题(单调队列窗口最值)、第 84 题(堆)同一「堆/选择」思维;与第 2 题(复杂度)呼应:每次 O(log K) 才能撑住数据流。
  • 工程实现:Java PriorityQueue 做小顶堆门槛;海量日志 TopK=分治+堆;监控系统实时热点、推荐系统候选粗排、排行榜、限流热点 Key 统计。分布式场景:单机堆 + 汇总再堆。
  • 408 / 国企真题:互联网面试/笔试「数据流 TopK 最优结构」标准答案大小为 K 的堆;国企若考「在数组中找第 k 大」也可用快速选择平均 O(n) 或小顶堆 O(n log k)。
  • 面试追问:①「为何小顶堆不是大顶堆?」(堆顶是门槛=当前第 K 大,新元素比门槛大才替换);②「全量数据可在内存时?」(快速选择/建大顶堆弹 K 次也可,但数据流必须增量堆);③「复杂度?」(n 个流元素共 O(n log K),空间 O(K))。

【拓展延伸】

变式问法:①「数据流 TopK 最优?」(大小为 K 的堆,本题);②「静态数组第 k 大?」(快速选择平均 O(n));③「求 TopK 且要按序输出?」(结束后对 K 个元素排序或用平衡结构)。

工程映射:热搜榜、监控告警 top 异常、安全审计高频 IP、数据库慢查询 TopN,都是流式或准流式 TopK。

易错提醒:数据流不能「每次全量重排」;BST 理论可行但淘汰语义不如堆直接;哈希表不维护顺序,无法直接出 TopK。


94. 以下排序算法中,不需要进行元素比较的是( ) ​

标签:【互联网/国企笔试高频】

A. 快速排序 B. 冒泡排序 C. 归并排序 D. 基数排序

答案:D

【结论】 基数排序不需要元素比较,选 D。

【逐项辨析】

  • A 快速排序:基于比较(比较 a[i] 与 pivot)。
  • D 基数排序:正确。 按位数逐位分配和收集,不进行比较。
  • C 归并排序:基于比较(合并时比较两子序列当前元素)。
  • B 冒泡排序:基于比较(相邻元素比较)。

【知识点】 比较排序 vs 非比较排序

类别算法时间复杂度下界
比较排序冒泡、插入、选择、希尔、快排、归并、堆排最坏 Ω(n log n)O(n log n)
非比较排序基数排序、计数排序、桶排序可达 O(n)无此下界

为什么比较排序有 O(n log n) 的下界? 用决策树模型证明

n 个元素共有 n! 种排列,每次比较只有 2 个结果(是 / 否)
决策树至少需要 n! 个叶子 → 树高 ≥ log₂(n!)
由斯特林公式:log₂(n!) ≈ n log₂n
→ 任何比较排序至少需要 Ω(n log n) 次比较

非比较排序绕开了“比较”这一操作,因此可以突破该下界 —— 但代价是需要额外的空间与对关键字形式的限制。

【记忆锚点】 “比较排序下界 n log n;不比较的基数 / 计数 / 桶排序能到 O(n)”。

【易混对比】 三种非比较排序的适用场景

算法适用条件复杂度
基数排序关键字可分解为有限位数O(d(n+r))
计数排序关键字范围小且为整数O(n + k)
桶排序数据均匀分布平均 O(n)

【自测】 为什么比较排序无法突破 O(n log n) 的下界?

答:因为用决策树模型可以证明:n 个元素有 n! 种排列,决策树至少有 n! 个叶子,树高至少为 log₂(n!) ≈ n log₂n。408 与互联网笔试高频。

【知识关联】

  • 同库连考:与第 92 题(基数排序复杂度 O(d(n+r)))同一算法;与第 79/82/83/86/91 题(比较排序的复杂度、稳定性、空间)构成「比较 vs 非比较」对照;与第 2/3 题(复杂度下界概念)衔接。
  • 工程实现:整数/定长短键可用计数、基数、桶排序突破 n log n;Java 对象排序因无法「按位拆」且要稳定,仍走 TimSort;数据库对小范围整数列、内存引擎 sort 会选计数/基数路径。
  • 408 / 国企真题:408「下列排序中不基于比较的是」答案基数/计数/桶排序;「比较排序下界」Ω(n log n) 必背。国网判断题:「所有排序算法时间复杂度都不低于 O(n log n)」——错,非比较排序可以更低。
  • 面试追问:①「基数为何不比较?」(按位分配+收集);②「下界证明思路?」(决策树 n! 叶子,高 ≥ log₂(n!));③「非比较排序代价?」(额外空间 O(n+r)/O(n+k),且对关键字形式有限制)。

【拓展延伸】

变式问法:①「不需要元素比较的是?」(基数排序,本题);②「计数排序适合?」(关键字范围小的整数);③「桶排序适合?」(近似均匀分布)。

工程映射:IP/工号/卡号按位排序、多关键字稳定分位处理、限流计数器、旧式打孔卡基数排序是该思想的历史原型。

易错提醒:「不比较」≠「不花时间」,是分配收集的代价;D 正确的同时要知道它稳定且复杂度含 d、r。比较排序下界不要套到基数上。


持续学习,持续积累。