Update the sorting algorithms.

This commit is contained in:
krahets 2023-05-24 00:05:12 +08:00
parent a2d2011c78
commit 588980af86
7 changed files with 23 additions and 38 deletions

View file

@ -164,8 +164,6 @@
## 算法特性 ## 算法特性
各轮“冒泡”遍历的数组长度依次为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ ,总和为 $\frac{(n - 1) n}{2}$ **因此时间复杂度为 $O(n^2)$** 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ **是“自适应排序”**。 - **时间复杂度为 $O(n^2)$ 、自适应排序** :各轮“冒泡”遍历的数组长度依次为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ ,总和为 $\frac{(n - 1) n}{2}$ 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ 。
- **空间复杂度为 $O(1)$ 、原地排序**:指针 $i$ , $j$ 使用常数大小的额外空间。
指针 $i$ , $j$ 使用常数大小的额外空间,**因此空间复杂度为 $O(1)$ ,是“原地排序”**。 - **稳定排序**:由于在“冒泡”中遇到相等元素不交换。
由于在“冒泡”中遇到相等元素不交换,**因此冒泡排序是“稳定排序”**。

View file

@ -80,13 +80,10 @@
## 算法特性 ## 算法特性
**时间复杂度 $O(n + k)$** :假设元素在各个桶内平均分布,那么每个桶内的元素数量为 $\frac{n}{k}$ 。假设排序单个桶使用 $O(\frac{n}{k} \log\frac{n}{k})$ 时间,则排序所有桶使用 $O(n \log\frac{n}{k})$ 时间。**当桶数量 $k$ 比较大时,时间复杂度则趋向于 $O(n)$** 。合并结果时需要遍历 $n$ 个桶,花费 $O(k)$ 时间。 - **时间复杂度 $O(n + k)$** :假设元素在各个桶内平均分布,那么每个桶内的元素数量为 $\frac{n}{k}$ 。假设排序单个桶使用 $O(\frac{n}{k} \log\frac{n}{k})$ 时间,则排序所有桶使用 $O(n \log\frac{n}{k})$ 时间。**当桶数量 $k$ 比较大时,时间复杂度则趋向于 $O(n)$** 。合并结果时需要遍历 $n$ 个桶,花费 $O(k)$ 时间。
- **自适应排序**:在最坏情况下,所有数据被分配到一个桶中,且排序该桶使用 $O(n^2)$ 时间。
在最坏情况下,所有数据被分配到一个桶中,且排序该桶使用 $O(n^2)$ 时间,因此是“自适应排序”。 - **空间复杂度 $O(n + k)$ 、非原地排序** :需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间。
- 桶排序是否稳定取决于排序桶内元素的算法是否稳定。
**空间复杂度 $O(n + k)$** :需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间,属于“非原地排序”。
桶排序是否稳定取决于排序桶内元素的算法是否稳定。
## 如何实现平均分配 ## 如何实现平均分配

View file

@ -181,11 +181,9 @@ $$
## 算法特性 ## 算法特性
**时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 - **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。
- **空间复杂度 $O(n + m)$ 、非原地排序** :借助了长度分别为 $n$ 和 $m$ 的数组 `res``counter`
**空间复杂度 $O(n + m)$** :借助了长度分别为 $n$ 和 $m$ 的数组 `res``counter` ,因此是“非原地排序”。 - **稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现稳定排序。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是非稳定的。
**稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现“稳定排序”。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是“非稳定”的。
## 局限性 ## 局限性

View file

@ -81,11 +81,9 @@
## 算法特性 ## 算法特性
**时间复杂度 $O(n^2)$** :最差情况下,每次插入操作分别需要循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和得到 $\frac{(n - 1) n}{2}$ ,因此时间复杂度为 $O(n^2)$ 。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ ,因此是“自适应排序”。 - **时间复杂度 $O(n^2)$ 、自适应排序** :最差情况下,每次插入操作分别需要循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和得到 $\frac{(n - 1) n}{2}$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。
- **空间复杂度 $O(1)$ 、原地排序** :指针 $i$ , $j$ 使用常数大小的额外空间。
**空间复杂度 $O(1)$** :指针 $i$ , $j$ 使用常数大小的额外空间,所以插入排序是“原地排序”。 - **稳定排序**:在插入操作过程中,我们会将元素插入到相等元素的右侧,不会改变它们的顺序。
在插入操作过程中,我们会将元素插入到相等元素的右侧,不会改变它们的顺序,因此是“稳定排序”。
## 插入排序优势 ## 插入排序优势

View file

@ -138,11 +138,9 @@
## 算法特性 ## 算法特性
**时间复杂度 $O(n \log n)$** :划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。 - **时间复杂度 $O(n \log n)$ 、非自适应排序** :划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。
- **空间复杂度 $O(n)$ 、非原地排序** :递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间。
**空间复杂度 $O(n)$** :递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间;合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间;因此是“非原地排序”。 - **稳定排序**:在合并过程中,相等元素的次序保持不变。
在合并过程中,相等元素的次序保持不变,因此归并排序是“稳定排序”。
## 链表排序 * ## 链表排序 *

View file

@ -187,13 +187,9 @@
## 算法特性 ## 算法特性
**时间复杂度 $O(n \log n)$** :在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。 - **时间复杂度 $O(n \log n)$ 、自适应排序** :在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。
- **空间复杂度 $O(n)$ 、原地排序** :在输入数组完全倒序的情况下,达到最差递归深度 $n$ ,使用 $O(n)$ 栈帧空间。排序操作是在原数组上进行的,未借助额外数组。
在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间;因此快速排序是“自适应排序”。 - **非稳定排序**:在哨兵划分的最后一步,基准数可能会被交换至相等元素的右侧。
**空间复杂度 $O(n)$** :在输入数组完全倒序的情况下,达到最差递归深度 $n$ 。由于未使用辅助数组,因此算法是“原地排序”。
在哨兵划分的最后一步,基准数可能会被交换至相等元素的右侧,因此是“非稳定排序”。
## 快排为什么快? ## 快排为什么快?

View file

@ -130,8 +130,8 @@ $$
## 算法特性 ## 算法特性
**时间复杂度 $O(nk)$** :设数据量为 $n$ 、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 相较于计数排序,基数排序适用于数值范围较大的情况,**但前提是数据必须可以表示为固定位数的格式,且位数不能过大**。例如,浮点数不适合使用基数排序,因为其位数 $k$ 过大,可能导致时间复杂度 $O(nk) \gg O(n^2)$ 。
**空间复杂度 $O(n + d)$** :与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res``counter` ,因此它是一种“非原地排序” - **时间复杂度 $O(nk)$** :设数据量为 $n$ 、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$
- **空间复杂度 $O(n + d)$ 、非原地排序** :与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res``counter`
基数排序与计数排序一样,都属于稳定排序。相较于计数排序,基数排序适用于数值范围较大的情况,**但前提是数据必须可以表示为固定位数的格式,且位数不能过大**。例如,浮点数不适合使用基数排序,因为其位数 $k$ 过大,可能导致时间复杂度 $O(nk) \gg O(n^2)$ - **稳定排序**:与计数排序相同