Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/00-排序算法文档编写规范.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 排序算法文档编写规范

本文档用于约束 `docs/` 目录下排序算法教学资料的统一写法,确保现有文档与后续新增文档在命名、结构、术语和语气上保持一致。

#### 文档命名应遵循什么规则?

所有排序算法教学文档都应使用“编号 + 中文标题 + `.md`”的命名方式,例如 `01-冒泡排序.md`、`15-TopK问题与排序选择.md`。编号应尽量连续,便于在 `docs/` 目录中按学习顺序阅读。

#### 文档标题和语言应如何统一?

一级标题应以中文主题名为主,必要时可在括号中补充英文名称,例如“快速排序(Quick Sort)”。正文内容必须使用中文撰写,避免出现整段英文说明,以保证资料适合课程展示、学生阅读和毕业设计答辩使用。

#### 推荐的文档结构是什么?

每篇文档建议遵循如下结构:(1)标题;(2)开篇导语,说明该主题在排序知识体系中的位置;(3)若干个“问题 + 回答”形式的主体小节;(4)结尾总结或教学提问建议。对于基础算法文档,重点回答“原理、复杂度、稳定性、优缺点、适用场景”;对于专题文档,可围绕“工程实践、应用场景、教学设计、学习路径”等展开。

#### 术语表达需要统一哪些内容?

所有文档都应尽量统一使用以下术语:时间复杂度、空间复杂度、稳定性、原地排序、自适应性、比较排序、非比较排序、外部排序、内部排序。对于同一概念,不应在不同文档中给出相互冲突的定义。复杂度说明优先按照“最好 / 平均 / 最坏 / 空间复杂度 / 稳定性”的顺序组织。

#### 文档语气和风格应保持什么特点?

整体风格应偏教学讲解型,面向排序算法学习者,强调清晰、严谨、易读。内容既要能让学生直接阅读理解,也要适合后续被 RAG 系统切片入库,因此每个问题下的回答应尽量自洽、独立,不依赖过多上下文跳转。

#### 新增和修改文档时应注意什么?

修改现有文档时,以补充和统一为主,不做无必要的大幅重写。新增文档时,应优先补足现有知识体系中的空白专题,避免重复已有文档已经详细覆盖的内容。完成后应复查文件名、一级标题、结构层级和结尾样式,确保整组文档看起来像同一套教学资料。

#### 本规范如何使用?

当后续需要新增或修改排序算法教学文档时,应先参考本文档确定命名、结构和术语,再开始撰写或补充。若未来文档体系继续扩展,也应优先在本文档中同步更新约束,避免不同批次文档形成两套风格。
6 changes: 6 additions & 0 deletions docs/01-冒泡排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 冒泡排序(Bubble Sort)

冒泡排序通常是学习排序算法时接触到的第一种算法。它的价值不在于工程性能,而在于帮助初学者理解“比较、交换、稳定性、复杂度”这些排序算法最基础的概念,因此非常适合作为排序学习的起点。

#### 冒泡排序的基本原理是什么?

冒泡排序是一种简单的比较排序算法。它重复地遍历待排序的序列,依次比较相邻的两个元素,如果它们的顺序不符合排序要求(例如前者大于后者),则交换它们的位置。每一轮遍历结束后,当前未排序部分的最大元素会像气泡一样"浮"到序列的末尾,因此得名冒泡排序。经过 n-1 轮遍历后,整个序列就变得有序。其核心思想是通过相邻元素的逐步交换,将较大(或较小)的元素逐渐移动到正确的位置。
Expand All @@ -25,3 +27,7 @@
#### 冒泡排序适合在哪些场景中使用?

冒泡排序适合以下场景:(1)数据规模较小(n < 100)时,其简单性优于复杂算法带来的常数开销。(2)数据近乎有序时,结合提前终止优化可以接近 O(n) 的性能。(3)教学和算法学习场景,用于理解排序的基本思想和稳定性概念。(4)对稳定性有要求且数据量极少的嵌入式场景,代码简单不易出错。在实际工程中处理大规模数据时,应优先选择快速排序、归并排序等高效算法。

#### 本章小结与提问建议

冒泡排序的核心教学价值在于“简单、直观、易观察”,它适合帮助初学者建立对排序过程的第一印象,但并不适合作为大规模数据处理的首选算法。学习本章后,可以继续追问:“为什么插入排序通常比冒泡排序更快?”以及“稳定排序在真实业务中为什么重要?”
6 changes: 6 additions & 0 deletions docs/02-选择排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 选择排序(Selection Sort)

选择排序是另一种经典的入门排序算法。它和冒泡排序一样都属于 O(n²) 的简单排序,但它强调的是“每轮选出最值”的思路,因此很适合与冒泡排序、插入排序做横向比较,帮助学习者建立不同排序策略的直觉。

#### 选择排序的基本原理是什么?

选择排序的核心思想是:每一轮从未排序的部分中选出最小(或最大)的元素,将其放到已排序部分的末尾。具体过程为:第 1 轮从 n 个元素中选出最小元素与第 1 个位置的元素交换;第 2 轮从剩余的 n-1 个元素中选出最小元素与第 2 个位置的元素交换;以此类推,经过 n-1 轮后整个序列有序。选择排序的特点是每一轮只进行一次交换操作,因此数据交换次数为 O(n),比冒泡排序的交换次数少很多。
Expand All @@ -25,3 +27,7 @@
#### 选择排序适合在哪些场景中使用?

选择排序适合以下场景:(1)数据规模很小(n < 50)的情况,简单实现且性能差异不明显。(2)交换操作代价高的场景,例如排序包含大量数据字段的结构体或对象,选择排序的 O(n) 交换次数远优于冒泡排序。(3)内存极度受限的嵌入式环境,O(1) 空间且代码极简。(4)不需要排序稳定性的场景。在需要处理中等或大规模数据时,应优先选择时间复杂度更优的排序算法。

#### 本章小结与提问建议

选择排序的关键特点是比较次数固定、交换次数少,但它无法利用已有序性,也不具备稳定性。学习本章后,可以进一步思考:“如果交换代价很高,为什么选择排序仍然可能有价值?”以及“选择排序和冒泡排序的本质区别是什么?”
6 changes: 6 additions & 0 deletions docs/03-插入排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 插入排序(Insertion Sort)

插入排序是简单排序算法中最具工程直觉的一种。它模仿人们整理扑克牌的过程,既容易理解,又能体现“输入越接近有序,算法越快”的自适应思想,因此在教学和工程优化中都很重要。

#### 插入排序的基本原理是什么?

插入排序的核心思想类似于整理扑克牌:将序列分为已排序和未排序两部分,每次从未排序部分取出一个元素,在已排序部分中从后往前逐一比较,找到该元素应该插入的位置,将其插入到正确位置。具体地,初始时认为第一个元素已经有序,从第二个元素开始,将其与已排序部分的元素逐一比较,大于该元素的元素依次后移一位,直到找到不大于该元素的位置或到达序列头部,然后将元素插入到空出的位置。
Expand All @@ -25,3 +27,7 @@
#### 插入排序在实际工程中有哪些应用?

插入排序在实际工程中有广泛应用:(1)**TimSort 的组成部分**:Python 和 Java 的默认排序算法 TimSort 使用插入排序对小于 64 的子数组进行排序。(2)**IntroSort 的兜底**:C++ STL 的 `std::sort` 使用 IntroSort,当递归深度过大或子数组足够小时切换为插入排序。(3)**小规模数据排序**:当数据量小于某个阈值(通常 16~64)时,插入排序的常数因子优势使其快于递归开销较大的快速排序。(4)**在线数据流排序**:需要对流式数据进行实时排序维护时,插入排序的在线特性非常适合。

#### 本章小结与提问建议

插入排序展示了“简单算法不一定没有工程价值”这一点。它在大规模乱序数据上不够高效,但在小规模、近乎有序或需要在线处理的场景中非常有用。学习本章后,可以继续追问:“为什么高级排序算法常常在最后切换为插入排序?”
6 changes: 6 additions & 0 deletions docs/04-希尔排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 希尔排序(Shell Sort)

希尔排序可以看作插入排序的“加速版”。它通过增量分组的方式先让数据大致有序,再在最后一轮用插入排序完成收尾,因此非常适合用来理解“优化简单算法”的思路。

#### 希尔排序的基本原理是什么?

希尔排序是插入排序的改进版本,也称为"缩小增量排序"。其核心思想是:先将整个序列按照一个较大的增量(gap)分成若干个子序列,对每个子序列分别进行插入排序,使得元素可以快速跨越多个位置移动;然后逐步缩小增量,继续对各子序列进行插入排序;当增量缩小到 1 时,整个序列已经基本有序,此时进行最后一次标准插入排序即可快速完成。希尔排序的精髓在于,通过先处理间距较大的元素对比和移动,使序列快速趋于有序,从而克服了普通插入排序只能逐个移动元素的缺陷。
Expand All @@ -25,3 +27,7 @@
#### 希尔排序适合在哪些场景中使用?

希尔排序适合以下场景:(1)中等规模数据排序,当数据量在几千到几万之间时,希尔排序实现简单且性能优于 O(n²) 算法。(2)嵌入式系统或资源受限环境,不需要额外内存且代码实现紧凑。(3)不需要排序稳定性的场景。(4)作为快速排序的补充,某些实现中在子数组较小时切换为希尔排序。在现代工程实践中,希尔排序已较少被直接使用,更多作为算法教学中的重要一环。

#### 本章小结与提问建议

希尔排序的重点不只是记住某个复杂度,而是理解“通过增量让序列逐步接近有序”的思想。它连接了简单排序和高效排序之间的认知桥梁。学习本章后,可以继续思考:“增量序列为什么会显著影响性能?”
6 changes: 6 additions & 0 deletions docs/05-归并排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 归并排序(Merge Sort)

归并排序是分治思想在排序领域中最经典的代表。它不仅复杂度稳定,而且天然适合链表排序、外部排序和并行化处理,因此既是算法课程中的重点,也是工程实践中的重要基础。

#### 归并排序的基本原理是什么?

归并排序采用分治(Divide and Conquer)策略。它将序列递归地拆分为两个子序列,分别排序后再合并(Merge)。具体步骤为:(1)**分解**:将长度为 n 的序列从中间拆分为两个长度约为 n/2 的子序列。(2)**递归排序**:对两个子序列分别递归地应用归并排序。(3)**合并**:将两个已排序的子序列合并为一个有序序列。合并过程中,使用两个指针分别指向两个子序列的头部,每次选取较小的元素放入结果序列,直到所有元素合并完毕。递归的终止条件是子序列长度小于等于 1。
Expand All @@ -25,3 +27,7 @@
#### 归并排序和快速排序应该如何选择?

归并排序和快速排序各有适用场景:(1)**稳定性要求**:需要稳定排序时选归并排序,快速排序不稳定。(2)**最坏情况性能**:要求最坏情况也是 O(n log n) 时选归并排序,快速排序最坏为 O(n²)。(3)**内存限制**:内存紧张时选快速排序(O(log n) 栈空间),归并排序需要 O(n) 额外空间。(4)**平均性能**:在数组上的平均性能,经过优化的快速排序通常优于归并排序(缓存友好性更好)。(5)**外部排序**:数据无法全部载入内存时选归并排序。(6)**链表排序**:链表上归并排序是首选,快速排序在链表上效率较差。Java 对对象数组使用 TimSort(基于归并排序),对基本类型数组使用双轴快速排序。

#### 本章小结与提问建议

归并排序体现了“分而治之”的强大威力,它在最坏情况下仍能保持 O(n log n),同时兼具稳定性,因此在很多场景中比快速排序更可靠。学习本章后,可以继续追问:“为什么归并排序适合外部排序和链表排序?”
6 changes: 6 additions & 0 deletions docs/06-快速排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 快速排序(Quick Sort)

快速排序是实际工程中最具代表性的高效排序算法之一。它平均性能优秀、缓存友好、优化手段丰富,因此在标准库和面试题中都非常常见,也是学习分治思想和分区操作的重点内容。

#### 快速排序的基本原理是什么?

快速排序基于分治策略,其核心操作是"分区"(Partition)。具体步骤为:(1)**选取基准值(Pivot)**:从序列中选择一个元素作为基准值。(2)**分区操作**:将序列重新排列,使得所有小于基准值的元素放在基准值左边,所有大于基准值的元素放在基准值右边,基准值被放到最终正确的位置。(3)**递归排序**:对基准值左右两边的子序列递归地应用快速排序。与归并排序"先分后合"不同,快速排序是"先排后分"——在分区过程中就完成了元素的大致排序,不需要合并步骤。
Expand All @@ -25,3 +27,7 @@
#### 快速排序为什么在实际应用中比归并排序更受欢迎?

快速排序在实际工程中更受欢迎主要有以下原因:(1)**缓存友好性**:快速排序在连续内存上顺序访问数据,CPU 缓存命中率高;而归并排序需要在原数组和辅助数组之间来回复制,缓存利用率较低。(2)**空间效率**:快速排序是原地排序只需 O(log n) 栈空间,归并排序需要 O(n) 辅助空间。(3)**常数因子小**:快速排序的内循环(比较和交换)操作非常简单高效。(4)**优化成熟**:IntroSort 等变体已经消除了最坏情况的风险。然而在需要稳定性或外部排序的场景中,归并排序仍是更好的选择。

#### 本章小结与提问建议

快速排序的核心优势在于平均性能高、工程优化成熟,但它也有最坏情况退化和不稳定性的问题。学习本章后,可以进一步思考:“为什么随机化 Pivot 可以降低最坏情况风险?”以及“什么场景下应该优先选择归并排序而不是快速排序?”
6 changes: 6 additions & 0 deletions docs/07-堆排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 堆排序(Heap Sort)

堆排序展示了“数据结构如何直接服务于算法设计”。它利用堆这种完全二叉树结构,将“不断选出当前最大值”这个过程高效化,因此特别适合帮助学习者理解堆、优先队列和 O(n log n) 排序之间的关系。

#### 堆排序的基本原理是什么?

堆排序利用"堆"这种数据结构进行排序。堆是一棵完全二叉树,分为最大堆(每个节点的值大于等于其子节点)和最小堆(每个节点的值小于等于其子节点)。堆排序的过程分两个阶段:(1)**建堆**:将无序数组原地构建为一个最大堆,从最后一个非叶子节点开始,逐个执行"下沉"(Sift Down)操作,时间复杂度为 O(n)。(2)**排序**:反复将堆顶(最大值)与堆的末尾元素交换,然后将堆大小减 1 并对新的堆顶执行下沉操作以恢复堆性质。每次操作确定一个元素的最终位置,经过 n-1 次交换后,数组变为升序。整个堆排序利用了数组下标即可表示父子关系的特性,不需要额外的指针。
Expand All @@ -25,3 +27,7 @@
#### 堆排序和快速排序应该如何选择?

堆排序和快速排序的选择取决于具体需求:(1)**性能追求**:追求平均最快速度选快速排序,其缓存友好性和较小的常数因子使其在大多数情况下更快。(2)**最坏保证**:要求确定性的 O(n log n) 保证选堆排序,特别是在安全敏感场景中防止恶意输入导致的排序退化攻击。(3)**空间限制**:两者都是原地排序,但堆排序的 O(1) 优于快速排序的 O(log n) 栈空间。(4)**数据特征**:重复元素多时选三路快排;需要 Top-K 时选堆排序。(5)**最佳实践**:现代标准库通常使用 IntroSort,结合两者优点——先快排,深度过大时切堆排,小规模切插入排序。

#### 本章小结与提问建议

堆排序的价值在于同时满足“最坏 O(n log n)”和“额外空间 O(1)”,这使它在理论上非常重要。学习本章后,可以继续追问:“为什么堆排序虽然复杂度优秀,但实际常数因子通常大于快速排序?”以及“堆排序与 Top-K 问题之间有什么联系?”
6 changes: 6 additions & 0 deletions docs/08-基数排序.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 基数排序(Radix Sort)

基数排序是典型的非比较排序,它帮助学习者理解一个关键事实:排序并不总是依赖元素两两比较。在特定数据条件下,基数排序可以突破比较排序的 O(n log n) 下界,因此是扩展排序知识体系的重要一章。

#### 基数排序的基本原理是什么?

基数排序是一种非比较排序算法,它不通过元素之间的直接比较来确定顺序,而是根据元素的各个"位"(digit)来分配和收集。具体过程为:(1)确定所有元素的最大位数 d(例如最大数是 999,则 d=3)。(2)从最低位(个位)开始,依次对每一位进行稳定的分配和收集——将所有元素按照当前位的值分配到 0~9 共 10 个桶中,然后按桶的顺序依次收集元素。(3)对十位、百位...依次重复上述操作。经过 d 轮分配-收集后,序列变为有序。这种"从低位到高位"的方式称为 LSD(Least Significant Digit)基数排序;也有从高位到低位的 MSD 方式。
Expand All @@ -25,3 +27,7 @@
#### 基数排序适合在哪些场景中使用?

基数排序适合以下场景:(1)**大量整数排序**:排序百万到上亿级别的整数数据(如 ID、时间戳),当数据位数固定时效率极高。(2)**字符串排序**:对定长字符串(如电话号码、身份证号、IP 地址)进行排序。(3)**多关键字排序**:需要按多个关键字排序时(如先按年龄再按姓名),基数排序的多轮处理天然适合。(4)**数据库索引构建**:某些数据库引擎使用基数排序构建索引。(5)**后缀数组构建**:部分后缀数组构建算法使用基数排序作为基础组件。不适合的场景包括:浮点数排序(位表示与数值顺序不一致)、数据位数差异极大的场景、数据量很小的场景。

#### 本章小结与提问建议

基数排序的重要意义在于帮助学习者跳出“排序一定靠比较”的固有印象。它在整数、定长字符串等场景中效率极高,但适用条件也更严格。学习本章后,可以继续思考:“基数排序和计数排序、桶排序之间到底是什么关系?”
Loading
Loading