From 588980af86dd9fb6af2e051c2becaff905481b88 Mon Sep 17 00:00:00 2001 From: krahets Date: Wed, 24 May 2023 00:05:12 +0800 Subject: [PATCH] Update the sorting algorithms. --- docs/chapter_sorting/bubble_sort.md | 8 +++----- docs/chapter_sorting/bucket_sort.md | 11 ++++------- docs/chapter_sorting/counting_sort.md | 8 +++----- docs/chapter_sorting/insertion_sort.md | 8 +++----- docs/chapter_sorting/merge_sort.md | 8 +++----- docs/chapter_sorting/quick_sort.md | 10 +++------- docs/chapter_sorting/radix_sort.md | 8 ++++---- 7 files changed, 23 insertions(+), 38 deletions(-) diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index afb5cdd1d..d323de7ca 100755 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -164,8 +164,6 @@ ## 算法特性 -各轮“冒泡”遍历的数组长度依次为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ ,总和为 $\frac{(n - 1) n}{2}$ ,**因此时间复杂度为 $O(n^2)$** 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ ,**是“自适应排序”**。 - -指针 $i$ , $j$ 使用常数大小的额外空间,**因此空间复杂度为 $O(1)$ ,是“原地排序”**。 - -由于在“冒泡”中遇到相等元素不交换,**因此冒泡排序是“稳定排序”**。 +- **时间复杂度为 $O(n^2)$ 、自适应排序** :各轮“冒泡”遍历的数组长度依次为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ ,总和为 $\frac{(n - 1) n}{2}$ 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ 。 +- **空间复杂度为 $O(1)$ 、原地排序**:指针 $i$ , $j$ 使用常数大小的额外空间。 +- **稳定排序**:由于在“冒泡”中遇到相等元素不交换。 diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index e2d1d12cb..b872889bd 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -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^2)$ 时间,因此是“自适应排序”。 - -**空间复杂度 $O(n + k)$** :需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间,属于“非原地排序”。 - -桶排序是否稳定取决于排序桶内元素的算法是否稳定。 +- **时间复杂度 $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 + k)$ 、非原地排序** :需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间。 +- 桶排序是否稳定取决于排序桶内元素的算法是否稳定。 ## 如何实现平均分配 diff --git a/docs/chapter_sorting/counting_sort.md b/docs/chapter_sorting/counting_sort.md index d62bc79eb..22a239d9e 100644 --- a/docs/chapter_sorting/counting_sort.md +++ b/docs/chapter_sorting/counting_sort.md @@ -181,11 +181,9 @@ $$ ## 算法特性 -**时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 - -**空间复杂度 $O(n + m)$** :借助了长度分别为 $n$ 和 $m$ 的数组 `res` 和 `counter` ,因此是“非原地排序”。 - -**稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现“稳定排序”。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是“非稳定”的。 +- **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 +- **空间复杂度 $O(n + m)$ 、非原地排序** :借助了长度分别为 $n$ 和 $m$ 的数组 `res` 和 `counter` 。 +- **稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现稳定排序。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是非稳定的。 ## 局限性 diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 9435ef4fd..f2fa8d0cf 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -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(1)$** :指针 $i$ , $j$ 使用常数大小的额外空间,所以插入排序是“原地排序”。 - -在插入操作过程中,我们会将元素插入到相等元素的右侧,不会改变它们的顺序,因此是“稳定排序”。 +- **时间复杂度 $O(n^2)$ 、自适应排序** :最差情况下,每次插入操作分别需要循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和得到 $\frac{(n - 1) n}{2}$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。 +- **空间复杂度 $O(1)$ 、原地排序** :指针 $i$ , $j$ 使用常数大小的额外空间。 +- **稳定排序**:在插入操作过程中,我们会将元素插入到相等元素的右侧,不会改变它们的顺序。 ## 插入排序优势 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index a643ad8c0..8d8ff2c29 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -138,11 +138,9 @@ ## 算法特性 -**时间复杂度 $O(n \log n)$** :划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。 - -**空间复杂度 $O(n)$** :递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间;合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间;因此是“非原地排序”。 - -在合并过程中,相等元素的次序保持不变,因此归并排序是“稳定排序”。 +- **时间复杂度 $O(n \log n)$ 、非自适应排序** :划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。 +- **空间复杂度 $O(n)$ 、非原地排序** :递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间。 +- **稳定排序**:在合并过程中,相等元素的次序保持不变。 ## 链表排序 * diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 7bd5d60a7..0d5be05cb 100755 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -187,13 +187,9 @@ ## 算法特性 -**时间复杂度 $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 \log n)$ 、自适应排序** :在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 +- **空间复杂度 $O(n)$ 、原地排序** :在输入数组完全倒序的情况下,达到最差递归深度 $n$ ,使用 $O(n)$ 栈帧空间。排序操作是在原数组上进行的,未借助额外数组。 +- **非稳定排序**:在哨兵划分的最后一步,基准数可能会被交换至相等元素的右侧。 ## 快排为什么快? diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index 444cde80e..413c0bcca 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -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` ,因此它是一种“非原地排序”。 - -基数排序与计数排序一样,都属于稳定排序。相较于计数排序,基数排序适用于数值范围较大的情况,**但前提是数据必须可以表示为固定位数的格式,且位数不能过大**。例如,浮点数不适合使用基数排序,因为其位数 $k$ 过大,可能导致时间复杂度 $O(nk) \gg O(n^2)$ 。 +- **时间复杂度 $O(nk)$** :设数据量为 $n$ 、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 +- **空间复杂度 $O(n + d)$ 、非原地排序** :与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res` 和 `counter` 。 +- **稳定排序**:与计数排序相同。