1. lower-case nouns

2. fix 2 figures
3. Replace some 「」 by “”
This commit is contained in:
krahets
2023-08-20 23:28:30 +08:00
parent 2626de8d0b
commit 981144e42d
48 changed files with 174 additions and 162 deletions

View File

@ -1,6 +1,6 @@
# 冒泡排序
「冒泡排序 Bubble Sort」通过连续地比较与交换相邻元素实现排序。这个过程就像气泡从底部升到顶部一样因此得名冒泡排序。
「冒泡排序 bubble sort」通过连续地比较与交换相邻元素实现排序。这个过程就像气泡从底部升到顶部一样因此得名冒泡排序。
我们可以利用元素交换操作模拟上述过程:从数组最左端开始向右遍历,依次比较相邻元素大小,如果“左元素 > 右元素”就交换它俩。遍历完成后,最大的元素会被移动到数组的最右端。

View File

@ -2,7 +2,7 @@
前述的几种排序算法都属于“基于比较的排序算法”,它们通过比较元素间的大小来实现排序。此类排序算法的时间复杂度无法超越 $O(n \log n)$ 。接下来,我们将探讨几种“非比较排序算法”,它们的时间复杂度可以达到线性阶。
「桶排序 Bucket Sort」是分治思想的一个典型应用。它通过设置一些具有大小顺序的桶每个桶对应一个数据范围将数据平均分配到各个桶中然后在每个桶内部分别执行排序最终按照桶的顺序将所有数据合并。
「桶排序 bucket sort」是分治思想的一个典型应用。它通过设置一些具有大小顺序的桶每个桶对应一个数据范围将数据平均分配到各个桶中然后在每个桶内部分别执行排序最终按照桶的顺序将所有数据合并。
## 算法流程

View File

@ -1,6 +1,6 @@
# 计数排序
「计数排序 Counting Sort」通过统计元素数量来实现排序通常应用于整数数组。
「计数排序 counting sort」通过统计元素数量来实现排序通常应用于整数数组。
## 简单实现
@ -92,7 +92,7 @@
细心的同学可能发现,**如果输入数据是对象,上述步骤 `3.` 就失效了**。例如,输入数据是商品对象,我们想要按照商品价格(类的成员变量)对商品进行排序,而上述算法只能给出价格的排序结果。
那么如何才能得到原数据的排序结果呢?我们首先计算 `counter` 的前缀和。顾名思义,索引 `i` 处的前缀和 `prefix[i]` 等于数组前 `i` 个元素之和,即
那么如何才能得到原数据的排序结果呢?我们首先计算 `counter` 的前缀和。顾名思义,索引 `i` 处的前缀和 `prefix[i]` 等于数组前 `i` 个元素之和,即
$$
\text{prefix}[i] = \sum_{j=0}^i \text{counter[j]}

View File

@ -2,9 +2,9 @@
!!! tip
阅读本节前,请确保已学完「堆」章节。
阅读本节前,请确保已学完“堆“章节。
「堆排序 Heap Sort」是一种基于堆数据结构实现的高效排序算法。我们可以利用已经学过的“建堆操作”和“元素出堆操作”实现堆排序
「堆排序 heap sort」是一种基于堆数据结构实现的高效排序算法。我们可以利用已经学过的“建堆操作”和“元素出堆操作”实现堆排序
1. 输入数组并建立小顶堆,此时最小元素位于堆顶。
2. 不断执行出堆操作,依次记录出堆元素,即可得到从小到大排序的序列。

View File

@ -1,6 +1,6 @@
# 插入排序
「插入排序 Insertion Sort」是一种简单的排序算法它的工作原理与手动整理一副牌的过程非常相似。
「插入排序 insertion sort」是一种简单的排序算法它的工作原理与手动整理一副牌的过程非常相似。
具体来说,我们在未排序区间选择一个基准元素,将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置。

View File

@ -1,6 +1,6 @@
# 归并排序
「归并排序 Merge Sort」基于分治思想实现排序包含“划分”和“合并”两个阶段
「归并排序 merge sort」基于分治思想实现排序包含“划分”和“合并”两个阶段
1. **划分阶段**:通过递归不断地将数组从中点处分开,将长数组的排序问题转换为短数组的排序问题。
2. **合并阶段**:当子数组长度为 1 时终止划分,开始合并,持续地将左右两个较短的有序数组合并为一个较长的有序数组,直至结束。

View File

@ -1,8 +1,8 @@
# 快速排序
「快速排序 Quick Sort」是一种基于分治思想的排序算法运行高效应用广泛。
「快速排序 quick sort」是一种基于分治思想的排序算法运行高效应用广泛。
快速排序的核心操作是哨兵划分,其目标是:选择数组中的某个元素作为“基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧。具体来说,哨兵划分的流程为:
快速排序的核心操作是哨兵划分,其目标是:选择数组中的某个元素作为“基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧。具体来说,哨兵划分的流程为:
1. 选取数组最左端元素作为基准数,初始化两个指针 `i``j` 分别指向数组的两端。
2. 设置一个循环,在每轮中使用 `i``j`)分别寻找第一个比基准数大(小)的元素,然后交换这两个元素。
@ -133,8 +133,8 @@
## 算法流程
1. 首先,对原数组执行一次哨兵划分,得到未排序的左子数组和右子数组。
2. 然后,对左子数组和右子数组分别递归执行哨兵划分
1. 首先,对原数组执行一次哨兵划分,得到未排序的左子数组和右子数组。
2. 然后,对左子数组和右子数组分别递归执行哨兵划分
3. 持续递归,直至子数组长度为 1 时终止,从而完成整个数组的排序。
![快速排序流程](quick_sort.assets/quick_sort_overview.png)
@ -219,15 +219,15 @@
## 快排为什么快?
从名称上就能看出,快速排序在效率方面应该具有一定的优势。尽管快速排序的平均时间复杂度与归并排序」和「堆排序相同,但通常快速排序的效率更高,原因如下:
从名称上就能看出,快速排序在效率方面应该具有一定的优势。尽管快速排序的平均时间复杂度与归并排序”和“堆排序相同,但通常快速排序的效率更高,原因如下:
- **出现最差情况的概率很低**:虽然快速排序的最差时间复杂度为 $O(n^2)$ ,没有归并排序稳定,但在绝大多数情况下,快速排序能在 $O(n \log n)$ 的时间复杂度下运行。
- **缓存使用效率高**:在执行哨兵划分操作时,系统可将整个子数组加载到缓存,因此访问元素的效率较高。而像堆排序这类算法需要跳跃式访问元素,从而缺乏这一特性。
- **复杂度的常数系数低**:在上述三种算法中,快速排序的比较、赋值、交换等操作的总数量最少。这与插入排序」比「冒泡排序更快的原因类似。
- **缓存使用效率高**:在执行哨兵划分操作时,系统可将整个子数组加载到缓存,因此访问元素的效率较高。而像堆排序这类算法需要跳跃式访问元素,从而缺乏这一特性。
- **复杂度的常数系数低**:在上述三种算法中,快速排序的比较、赋值、交换等操作的总数量最少。这与插入排序”比“冒泡排序更快的原因类似。
## 基准数优化
**快速排序在某些输入下的时间效率可能降低**。举一个极端例子,假设输入数组是完全倒序的,由于我们选择最左端元素作为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,导致左子数组长度为 $n - 1$ 、右子数组长度为 $0$ 。如此递归下去,每轮哨兵划分后的右子数组长度都为 $0$ ,分治策略失效,快速排序退化为冒泡排序
**快速排序在某些输入下的时间效率可能降低**。举一个极端例子,假设输入数组是完全倒序的,由于我们选择最左端元素作为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,导致左子数组长度为 $n - 1$ 、右子数组长度为 $0$ 。如此递归下去,每轮哨兵划分后的右子数组长度都为 $0$ ,分治策略失效,快速排序退化为冒泡排序
为了尽量避免这种情况发生,**我们可以优化哨兵划分中的基准数的选取策略**。例如,我们可以随机选取一个元素作为基准数。然而,如果运气不佳,每次都选到不理想的基准数,效率仍然不尽如人意。

View File

@ -2,14 +2,14 @@
上一节我们介绍了计数排序,它适用于数据量 $n$ 较大但数据范围 $m$ 较小的情况。假设我们需要对 $n = 10^6$ 个学号进行排序,而学号是一个 $8$ 位数字,这意味着数据范围 $m = 10^8$ 非常大,使用计数排序需要分配大量内存空间,而基数排序可以避免这种情况。
「基数排序 Radix Sort」的核心思想与计数排序一致也通过统计个数来实现排序。在此基础上基数排序利用数字各位之间的递进关系依次对每一位进行排序从而得到最终的排序结果。
「基数排序 radix sort」的核心思想与计数排序一致也通过统计个数来实现排序。在此基础上基数排序利用数字各位之间的递进关系依次对每一位进行排序从而得到最终的排序结果。
## 算法流程
以学号数据为例,假设数字的最低位是第 $1$ 位,最高位是第 $8$ 位,基数排序的步骤如下:
1. 初始化位数 $k = 1$ 。
2. 对学号的第 $k$ 位执行计数排序。完成后,数据会根据第 $k$ 位从小到大排序。
2. 对学号的第 $k$ 位执行计数排序。完成后,数据会根据第 $k$ 位从小到大排序。
3. 将 $k$ 增加 $1$ ,然后返回步骤 `2.` 继续迭代,直到所有位都排序完成后结束。
![基数排序算法流程](radix_sort.assets/radix_sort_overview.png)

View File

@ -1,6 +1,6 @@
# 选择排序
「选择排序 Selection Sort」的工作原理非常直接开启一个循环每轮从未排序区间选择最小的元素将其放到已排序区间的末尾。
「选择排序 selection sort」的工作原理非常直接开启一个循环每轮从未排序区间选择最小的元素将其放到已排序区间的末尾。
设数组的长度为 $n$ ,选择排序的算法流程如下:

View File

@ -1,6 +1,6 @@
# 排序算法
「排序算法 Sorting Algorithm」用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用因为有序数据通常能够被更有效地查找、分析和处理。
「排序算法 sorting algorithm」用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用因为有序数据通常能够被更有效地查找、分析和处理。
在排序算法中,数据类型可以是整数、浮点数、字符或字符串等;顺序的判断规则可根据需求设定,如数字大小、字符 ASCII 码顺序或自定义规则。
@ -12,9 +12,9 @@
**就地性**:顾名思义,「原地排序」通过在原数组上直接操作实现排序,无须借助额外的辅助数组,从而节省内存。通常情况下,原地排序的数据搬运操作较少,运行速度也更快。
**稳定性**:「稳定排序」在完成排序后,相等元素在数组中的相对顺序不发生改变。稳定排序是优良特性,也是多级排序场景的必要条件。
**稳定性**:「稳定排序」在完成排序后,相等元素在数组中的相对顺序不发生改变。
假设我们有一个存储学生信息的表格,第 1, 2 列分别是姓名和年龄。在这种情况下,「非稳定排序」可能导致输入数据的有序性丧失。
稳定排序是多级排序场景的必要条件。假设我们有一个存储学生信息的表格,第 1 列和第 2 列分别是姓名和年龄。在这种情况下,「非稳定排序」可能导致输入数据的有序性丧失。
```shell
# 输入数据是按照姓名排序好的