This commit is contained in:
krahets
2025-07-10 07:14:11 +08:00
parent 718e8d4a1c
commit 85ebada8d9
29 changed files with 185 additions and 168 deletions

View File

@@ -2034,7 +2034,7 @@ comments: true
panic!("索引越界")
};
let num = self.arr[index];
// 将索引 index 之后的元素都向前移动一位
// 将索引 index 之后的元素都向前移动一位
for j in index..self.size - 1 {
self.arr[j] = self.arr[j + 1];
}

View File

@@ -16,13 +16,13 @@ comments: true
<div class="center-table" markdown>
| | 硬盘 | 内存 | 缓存 |
| ------ | ---------------------------------------- | -------------------------------------- | ------------------------------------------------- |
| 用途 | 长期存储数据,包括操作系统、程序、文件等 | 临时存储当前运行的程序和正在处理的数据 | 存储经常访问的数据和指令,减少 CPU 访问内存的次数 |
| 易失性 | 断电后数据不会丢失 | 断电后数据会丢失 | 断电后数据会丢失 |
| 容量 | 较大TB 级别 | 较小GB 级别 | 非常小MB 级别 |
| 速度 | 较慢,几百到几千 MB/s | 较快,几十 GB/s | 非常快,几十到几百 GB/s |
| 价格 | 较便宜,几毛到几元 / GB | 较贵,几十到几百元 / GB | 非常贵,随 CPU 打包计价 |
| | 硬盘 | 内存 | 缓存 |
| -------------- | ---------------------------------------- | -------------------------------------- | ------------------------------------------------- |
| 用途 | 长期存储数据,包括操作系统、程序、文件等 | 临时存储当前运行的程序和正在处理的数据 | 存储经常访问的数据和指令,减少 CPU 访问内存的次数 |
| 易失性 | 断电后数据不会丢失 | 断电后数据会丢失 | 断电后数据会丢失 |
| 容量 | 较大TB 级别 | 较小GB 级别 | 非常小MB 级别 |
| 速度 | 较慢,几百到几千 MB/s | 较快,几十 GB/s | 非常快,几十到几百 GB/s |
| 价格(人民币) | 较便宜,几毛到几元 / GB | 较贵,几十到几百元 / GB | 非常贵,随 CPU 打包计价 |
</div>

View File

@@ -578,7 +578,7 @@ comments: true
/* 递归的空间复杂度为 O(n) */
void recur(int n) {
if (n == 1) return;
return recur(n - 1);
recur(n - 1);
}
```
@@ -598,7 +598,7 @@ comments: true
/* 递归的空间复杂度为 O(n) */
void recur(int n) {
if (n == 1) return;
return recur(n - 1);
recur(n - 1);
}
```
@@ -727,7 +727,7 @@ comments: true
/* 递归的空间复杂度为 O(n) */
void recur(int n) {
if (n == 1) return;
return recur(n - 1);
recur(n - 1);
}
```
@@ -769,7 +769,7 @@ comments: true
/* 递归的空间复杂度为 O(n) */
void recur(int n) {
if (n == 1) return;
return recur(n - 1);
recur(n - 1);
}
```

View File

@@ -757,7 +757,7 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
若存在正实数 $c$ 和实数 $n_0$ ,使得对于所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为 $T(n) = O(f(n))$ 。
如图 2-8 所示,计算渐近上界就是寻找一个函数 $f(n)$ ,使得当 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别,仅相差一个常数 $c$ 的倍数
如图 2-8 所示,计算渐近上界就是寻找一个函数 $f(n)$ ,使得当 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别,仅相差一个常数系数 $c$。
![函数的渐近上界](time_complexity.assets/asymptotic_upper_bound.png){ class="animation-figure" }
@@ -771,9 +771,9 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
### 1. &nbsp; 第一步:统计操作数量
针对代码,逐行从上到下计算即可。然而,由于上述 $c \cdot f(n)$ 中的常数 $c$ 可以取任意大小,**因此操作数量 $T(n)$ 中的各种系数、常数项都可以忽略**。根据此原则,可以总结出以下计数简化技巧。
针对代码,逐行从上到下计算即可。然而,由于上述 $c \cdot f(n)$ 中的常数系数 $c$ 可以取任意大小,**因此操作数量 $T(n)$ 中的各种系数、常数项都可以忽略**。根据此原则,可以总结出以下计数简化技巧。
1. **忽略 $T(n)$ 中的常数**。因为它们都与 $n$ 无关,所以对时间复杂度不产生影响。
1. **忽略 $T(n)$ 中的常数**。因为它们都与 $n$ 无关,所以对时间复杂度不产生影响。
2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次等,都可以简化记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度没有影响。
3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用第 `1.` 点和第 `2.` 点的技巧。

View File

@@ -410,8 +410,8 @@ $$
### 3. &nbsp; 正确性证明
使用反证法,只分析 $n \geq 3$ 的情况。
使用反证法,只分析 $n \geq 4$ 的情况。
1. **所有因子 $\leq 3$** :假设最优切分方案中存在 $\geq 4$ 的因子 $x$ ,那么一定可以将其继续划分为 $2(x-2)$ ,从而获得更大的乘积。这与假设矛盾。
1. **所有因子 $\leq 3$** :假设最优切分方案中存在 $\geq 4$ 的因子 $x$ ,那么一定可以将其继续划分为 $2(x-2)$ ,从而获得更大(或相等)的乘积。这与假设矛盾。
2. **切分方案不包含 $1$** :假设最优切分方案中存在一个因子 $1$ ,那么它一定可以合并入另外一个因子中,以获得更大的乘积。这与假设矛盾。
3. **切分方案最多包含两个 $2$** :假设最优切分方案中包含三个 $2$ ,那么一定可以替换为两个 $3$ ,乘积更大。这与假设矛盾。

View File

@@ -1161,7 +1161,7 @@ comments: true
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20median_three%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E9%80%89%E5%8F%96%E4%B8%89%E4%B8%AA%E5%80%99%E9%80%89%E5%85%83%E7%B4%A0%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0%22%22%22%0A%20%20%20%20l,%20m,%20r%20%3D%20nums%5Bleft%5D,%20nums%5Bmid%5D,%20nums%5Bright%5D%0A%20%20%20%20if%20%28l%20%3C%3D%20m%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20m%20%3C%3D%20l%29%3A%0A%20%20%20%20%20%20%20%20return%20mid%20%20%23%20m%20%E5%9C%A8%20l%20%E5%92%8C%20r%20%E4%B9%8B%E9%97%B4%0A%20%20%20%20if%20%28m%20%3C%3D%20l%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20l%20%3C%3D%20m%29%3A%0A%20%20%20%20%20%20%20%20return%20left%20%20%23%20l%20%E5%9C%A8%20m%20%E5%92%8C%20r%20%E4%B9%8B%E9%97%B4%0A%20%20%20%20return%20right%0A%0Adef%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%93%A8%E5%85%B5%E5%88%92%E5%88%86%EF%BC%88%E4%B8%89%E6%95%B0%E5%8F%96%E4%B8%AD%E5%80%BC%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E4%B8%BA%E5%9F%BA%E5%87%86%E6%95%B0%0A%20%20%20%20med%20%3D%20median_three%28nums,%20left,%20%28left%20%2B%20right%29%20//%202,%20right%29%0A%20%20%20%20%23%20%E5%B0%86%E4%B8%AD%E4%BD%8D%E6%95%B0%E4%BA%A4%E6%8D%A2%E8%87%B3%E6%95%B0%E7%BB%84%E6%9C%80%E5%B7%A6%E7%AB%AF%0A%20%20%20%20nums%5Bleft%5D,%20nums%5Bmed%5D%20%3D%20nums%5Bmed%5D,%20nums%5Bleft%5D%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E4%B8%BA%E5%9F%BA%E5%87%86%E6%95%B0%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20%E4%BB%8E%E5%8F%B3%E5%90%91%E5%B7%A6%E6%89%BE%E9%A6%96%E4%B8%AA%E5%B0%8F%E4%BA%8E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20%E4%BB%8E%E5%B7%A6%E5%90%91%E5%8F%B3%E6%89%BE%E9%A6%96%E4%B8%AA%E5%A4%A7%E4%BA%8E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20%E5%B0%86%E5%9F%BA%E5%87%86%E6%95%B0%E4%BA%A4%E6%8D%A2%E8%87%B3%E4%B8%A4%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E5%88%86%E7%95%8C%E7%BA%BF%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20%E8%BF%94%E5%9B%9E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E4%B8%AD%E4%BD%8D%E5%9F%BA%E5%87%86%E6%95%B0%E4%BC%98%E5%8C%96%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20partition%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%93%A8%E5%85%B5%E5%88%92%E5%88%86%EF%BC%88%E4%B8%AD%E4%BD%8D%E5%9F%BA%E5%87%86%E6%95%B0%E4%BC%98%E5%8C%96%EF%BC%89%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20median_three%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E9%80%89%E5%8F%96%E4%B8%89%E4%B8%AA%E5%80%99%E9%80%89%E5%85%83%E7%B4%A0%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0%22%22%22%0A%20%20%20%20l,%20m,%20r%20%3D%20nums%5Bleft%5D,%20nums%5Bmid%5D,%20nums%5Bright%5D%0A%20%20%20%20if%20%28l%20%3C%3D%20m%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20m%20%3C%3D%20l%29%3A%0A%20%20%20%20%20%20%20%20return%20mid%20%20%23%20m%20%E5%9C%A8%20l%20%E5%92%8C%20r%20%E4%B9%8B%E9%97%B4%0A%20%20%20%20if%20%28m%20%3C%3D%20l%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20l%20%3C%3D%20m%29%3A%0A%20%20%20%20%20%20%20%20return%20left%20%20%23%20l%20%E5%9C%A8%20m%20%E5%92%8C%20r%20%E4%B9%8B%E9%97%B4%0A%20%20%20%20return%20right%0A%0Adef%20partition%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%93%A8%E5%85%B5%E5%88%92%E5%88%86%EF%BC%88%E4%B8%89%E6%95%B0%E5%8F%96%E4%B8%AD%E5%80%BC%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E4%B8%BA%E5%9F%BA%E5%87%86%E6%95%B0%0A%20%20%20%20med%20%3D%20median_three%28nums,%20left,%20%28left%20%2B%20right%29%20//%202,%20right%29%0A%20%20%20%20%23%20%E5%B0%86%E4%B8%AD%E4%BD%8D%E6%95%B0%E4%BA%A4%E6%8D%A2%E8%87%B3%E6%95%B0%E7%BB%84%E6%9C%80%E5%B7%A6%E7%AB%AF%0A%20%20%20%20nums%5Bleft%5D,%20nums%5Bmed%5D%20%3D%20nums%5Bmed%5D,%20nums%5Bleft%5D%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E4%B8%BA%E5%9F%BA%E5%87%86%E6%95%B0%0A%20%20%20%20i,%20j%20%3D%20left,%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20%E4%BB%8E%E5%8F%B3%E5%90%91%E5%B7%A6%E6%89%BE%E9%A6%96%E4%B8%AA%E5%B0%8F%E4%BA%8E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20%E4%BB%8E%E5%B7%A6%E5%90%91%E5%8F%B3%E6%89%BE%E9%A6%96%E4%B8%AA%E5%A4%A7%E4%BA%8E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bj%5D%20%3D%20nums%5Bj%5D,%20nums%5Bi%5D%0A%20%20%20%20%23%20%E5%B0%86%E5%9F%BA%E5%87%86%E6%95%B0%E4%BA%A4%E6%8D%A2%E8%87%B3%E4%B8%A4%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E5%88%86%E7%95%8C%E7%BA%BF%0A%20%20%20%20nums%5Bi%5D,%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D,%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20%E8%BF%94%E5%9B%9E%E5%9F%BA%E5%87%86%E6%95%B0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E4%B8%AD%E4%BD%8D%E5%9F%BA%E5%87%86%E6%95%B0%E4%BC%98%E5%8C%96%0A%20%20%20%20nums%20%3D%20%5B2,%204,%201,%200,%203,%205%5D%0A%20%20%20%20partition%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%93%A8%E5%85%B5%E5%88%92%E5%88%86%EF%BC%88%E4%B8%AD%E4%BD%8D%E5%9F%BA%E5%87%86%E6%95%B0%E4%BC%98%E5%8C%96%EF%BC%89%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">全屏观看 ></a></div>
## 11.5.5 &nbsp; 递归优化
## 11.5.5 &nbsp; 递归深度优化
**在某些输入下,快速排序可能占用空间较多**。以完全有序的输入数组为例,设递归中的子数组长度为 $m$ ,每轮哨兵划分操作都将产生长度为 $0$ 的左子数组和长度为 $m - 1$ 的右子数组,这意味着每一层递归调用减少的问题规模非常小(只减少一个元素),递归树的高度会达到 $n - 1$ ,此时需要占用 $O(n)$ 大小的栈帧空间。
@@ -1171,7 +1171,7 @@ comments: true
```python title="quick_sort.py"
def quick_sort(self, nums: list[int], left: int, right: int):
"""快速排序(递归优化)"""
"""快速排序(递归深度优化)"""
# 子数组长度为 1 时终止
while left < right:
# 哨兵划分操作
@@ -1188,7 +1188,7 @@ comments: true
=== "C++"
```cpp title="quick_sort.cpp"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
void quickSort(vector<int> &nums, int left, int right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1209,7 +1209,7 @@ comments: true
=== "Java"
```java title="quick_sort.java"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
void quickSort(int[] nums, int left, int right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1230,7 +1230,7 @@ comments: true
=== "C#"
```csharp title="quick_sort.cs"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
void QuickSort(int[] nums, int left, int right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1251,7 +1251,7 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 快速排序(递归优化)*/
/* 快速排序(递归深度优化)*/
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
// 子数组长度为 1 时终止
for left < right {
@@ -1272,7 +1272,7 @@ comments: true
=== "Swift"
```swift title="quick_sort.swift"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
func quickSortTailCall(nums: inout [Int], left: Int, right: Int) {
var left = left
var right = right
@@ -1295,7 +1295,7 @@ comments: true
=== "JS"
```javascript title="quick_sort.js"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
quickSort(nums, left, right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1316,7 +1316,7 @@ comments: true
=== "TS"
```typescript title="quick_sort.ts"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
quickSort(nums: number[], left: number, right: number): void {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1337,7 +1337,7 @@ comments: true
=== "Dart"
```dart title="quick_sort.dart"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
void quickSort(List<int> nums, int left, int right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1358,7 +1358,7 @@ comments: true
=== "Rust"
```rust title="quick_sort.rs"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) {
// 子数组长度为 1 时终止
while left < right {
@@ -1379,7 +1379,7 @@ comments: true
=== "C"
```c title="quick_sort.c"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
void quickSortTailCall(int nums[], int left, int right) {
// 子数组长度为 1 时终止
while (left < right) {
@@ -1404,7 +1404,7 @@ comments: true
=== "Kotlin"
```kotlin title="quick_sort.kt"
/* 快速排序(递归优化) */
/* 快速排序(递归深度优化) */
fun quickSortTailCall(nums: IntArray, left: Int, right: Int) {
// 子数组长度为 1 时终止
var l = left
@@ -1427,7 +1427,7 @@ comments: true
=== "Ruby"
```ruby title="quick_sort.rb"
### 快速排序(递归优化)###
### 快速排序(递归深度优化)###
def quick_sort(nums, left, right)
# 子数组长度不为 1 时递归
while left < right
@@ -1448,7 +1448,7 @@ comments: true
=== "Zig"
```zig title="quick_sort.zig"
// 快速排序(递归优化)
// 快速排序(递归深度优化)
fn quickSort(nums: []i32, left_: usize, right_: usize) void {
var left = left_;
var right = right_;

View File

@@ -8,7 +8,7 @@ comments: true
- 冒泡排序通过交换相邻元素来实现排序。通过添加一个标志位来实现提前返回,我们可以将冒泡排序的最佳时间复杂度优化到 $O(n)$ 。
- 插入排序每轮将未排序区间内的元素插入到已排序区间的正确位置,从而完成排序。虽然插入排序的时间复杂度为 $O(n^2)$ ,但由于单元操作相对较少,因此在小数据量的排序任务中非常受欢迎。
- 快速排序基于哨兵划分操作实现排序。在哨兵划分中,有可能每次都选取到最差的基准数,导致时间复杂度劣化至 $O(n^2)$ 。引入中位数基准数或随机基准数可以降低这种劣化的概率。尾递归方法可以有效地减少递归深度,将空间复杂度优化到 $O(\log n)$ 。
- 快速排序基于哨兵划分操作实现排序。在哨兵划分中,有可能每次都选取到最差的基准数,导致时间复杂度劣化至 $O(n^2)$ 。引入中位数基准数或随机基准数可以降低这种劣化的概率。通过优先递归较短子区间,可有效减小递归深度,将空间复杂度优化到 $O(\log n)$ 。
- 归并排序包括划分和合并两个阶段,典型地体现了分治策略。在归并排序中,排序数组需要创建辅助数组,空间复杂度为 $O(n)$ ;然而排序链表的空间复杂度可以优化至 $O(1)$ 。
- 桶排序包含三个步骤:数据分桶、桶内排序和合并结果。它同样体现了分治策略,适用于数据体量很大的情况。桶排序的关键在于对数据进行平均分配。
- 计数排序是桶排序的一个特例,它通过统计数据出现的次数来实现排序。计数排序适用于数据量大但数据范围有限的情况,并且要求数据能够转换为正整数。
@@ -38,11 +38,11 @@ comments: true
再深入思考一下,如果我们选择 `nums[right]` 为基准数,那么正好反过来,必须先“从左往右查找”。
**Q**:关于尾递归优化,为什么选短的数组能保证递归深度不超过 $\log n$
**Q**:关于快速排序的递归深度优化,为什么选短的数组能保证递归深度不超过 $\log n$
递归深度就是当前未返回的递归方法的数量。每轮哨兵划分我们将原数组划分为两个子数组。在递归优化后,向下递归的子数组长度最大为原数组长度的一半。假设最差情况,一直为一半长度,那么最终的递归深度就是 $\log n$ 。
递归深度就是当前未返回的递归方法的数量。每轮哨兵划分我们将原数组划分为两个子数组。在递归深度优化后,向下递归的子数组长度最大为原数组长度的一半。假设最差情况,一直为一半长度,那么最终的递归深度就是 $\log n$ 。
回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 $n$、$n - 1$、$\dots$、$2$、$1$ ,递归深度为 $n$ 。递归优化可以避免这种情况出现。
回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 $n$、$n - 1$、$\dots$、$2$、$1$ ,递归深度为 $n$ 。递归深度优化可以避免这种情况出现。
**Q**:当数组中所有元素都相等时,快速排序的时间复杂度是 $O(n^2)$ 吗?该如何处理这种退化情况?

View File

@@ -1609,11 +1609,11 @@ comments: true
/* 判断双向队列是否为空 */
pub fn is_empty(&self) -> bool {
return self.size() == 0;
return self.que_size == 0;
}
/* 入队操作 */
pub fn push(&mut self, num: T, is_front: bool) {
fn push(&mut self, num: T, is_front: bool) {
let node = ListNode::new(num);
// 队首入队操作
if is_front {
@@ -1661,7 +1661,7 @@ comments: true
}
/* 出队操作 */
pub fn pop(&mut self, is_front: bool) -> Option<T> {
fn pop(&mut self, is_front: bool) -> Option<T> {
// 若队列为空,直接返回 None
if self.is_empty() {
return None;
@@ -3320,17 +3320,17 @@ comments: true
```rust title="array_deque.rs"
/* 基于环形数组实现的双向队列 */
struct ArrayDeque {
nums: Vec<i32>, // 用于存储双向队列元素的数组
struct ArrayDeque<T> {
nums: Vec<T>, // 用于存储双向队列元素的数组
front: usize, // 队首指针,指向队首元素
que_size: usize, // 双向队列长度
}
impl ArrayDeque {
impl<T: Copy + Default> ArrayDeque<T> {
/* 构造方法 */
pub fn new(capacity: usize) -> Self {
Self {
nums: vec![0; capacity],
nums: vec![T::default(); capacity],
front: 0,
que_size: 0,
}
@@ -3356,11 +3356,11 @@ comments: true
// 通过取余操作实现数组首尾相连
// 当 i 越过数组尾部后,回到头部
// 当 i 越过数组头部后,回到尾部
return ((i + self.capacity() as i32) % self.capacity() as i32) as usize;
((i + self.capacity() as i32) % self.capacity() as i32) as usize
}
/* 队首入队 */
pub fn push_first(&mut self, num: i32) {
pub fn push_first(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("双向队列已满");
return;
@@ -3374,7 +3374,7 @@ comments: true
}
/* 队尾入队 */
pub fn push_last(&mut self, num: i32) {
pub fn push_last(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("双向队列已满");
return;
@@ -3387,7 +3387,7 @@ comments: true
}
/* 队首出队 */
fn pop_first(&mut self) -> i32 {
fn pop_first(&mut self) -> T {
let num = self.peek_first();
// 队首指针向后移动一位
self.front = self.index(self.front as i32 + 1);
@@ -3396,14 +3396,14 @@ comments: true
}
/* 队尾出队 */
fn pop_last(&mut self) -> i32 {
fn pop_last(&mut self) -> T {
let num = self.peek_last();
self.que_size -= 1;
num
}
/* 访问队首元素 */
fn peek_first(&self) -> i32 {
fn peek_first(&self) -> T {
if self.is_empty() {
panic!("双向队列为空")
};
@@ -3411,7 +3411,7 @@ comments: true
}
/* 访问队尾元素 */
fn peek_last(&self) -> i32 {
fn peek_last(&self) -> T {
if self.is_empty() {
panic!("双向队列为空")
};
@@ -3421,9 +3421,9 @@ comments: true
}
/* 返回数组用于打印 */
fn to_array(&self) -> Vec<i32> {
fn to_array(&self) -> Vec<T> {
// 仅转换有效长度范围内的列表元素
let mut res = vec![0; self.que_size];
let mut res = vec![T::default(); self.que_size];
let mut j = self.front;
for i in 0..self.que_size {
res[i] = self.nums[self.index(j as i32)];

View File

@@ -1036,7 +1036,7 @@ comments: true
/* 判断队列是否为空 */
pub fn is_empty(&self) -> bool {
return self.size() == 0;
return self.que_size == 0;
}
/* 入队 */
@@ -2082,18 +2082,18 @@ comments: true
```rust title="array_queue.rs"
/* 基于环形数组实现的队列 */
struct ArrayQueue {
nums: Vec<i32>, // 用于存储队列元素的数组
struct ArrayQueue<T> {
nums: Vec<T>, // 用于存储队列元素的数组
front: i32, // 队首指针,指向队首元素
que_size: i32, // 队列长度
que_capacity: i32, // 队列容量
}
impl ArrayQueue {
impl<T: Copy + Default> ArrayQueue<T> {
/* 构造方法 */
fn new(capacity: i32) -> ArrayQueue {
fn new(capacity: i32) -> ArrayQueue<T> {
ArrayQueue {
nums: vec![0; capacity as usize],
nums: vec![T::default(); capacity as usize],
front: 0,
que_size: 0,
que_capacity: capacity,
@@ -2116,7 +2116,7 @@ comments: true
}
/* 入队 */
fn push(&mut self, num: i32) {
fn push(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("队列已满");
return;
@@ -2130,7 +2130,7 @@ comments: true
}
/* 出队 */
fn pop(&mut self) -> i32 {
fn pop(&mut self) -> T {
let num = self.peek();
// 队首指针向后移动一位,若越过尾部,则返回到数组头部
self.front = (self.front + 1) % self.que_capacity;
@@ -2139,7 +2139,7 @@ comments: true
}
/* 访问队首元素 */
fn peek(&self) -> i32 {
fn peek(&self) -> T {
if self.is_empty() {
panic!("index out of bounds");
}
@@ -2147,10 +2147,10 @@ comments: true
}
/* 返回数组 */
fn to_vector(&self) -> Vec<i32> {
fn to_vector(&self) -> Vec<T> {
let cap = self.que_capacity;
let mut j = self.front;
let mut arr = vec![0; self.que_size as usize];
let mut arr = vec![T::default(); cap as usize];
for i in 0..self.que_size {
arr[i as usize] = self.nums[(j % cap) as usize];
j += 1;

View File

@@ -977,13 +977,17 @@ comments: true
}
/* 将 List 转化为 Array 并返回 */
pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {
if let Some(node) = head {
let mut nums = self.to_array(node.borrow().next.as_ref());
nums.push(node.borrow().val);
return nums;
pub fn to_array(&self) -> Vec<T> {
fn _to_array<T: Sized + Copy>(head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {
if let Some(node) = head {
let mut nums = _to_array(node.borrow().next.as_ref());
nums.push(node.borrow().val);
return nums;
}
return Vec::new();
}
return Vec::new();
_to_array(self.peek())
}
}
```

View File

@@ -347,7 +347,7 @@
<!-- contributors -->
<div style="margin: 2em auto;">
<h3>贡献者</h3>
<p>本书在开源社区一百多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!</p>
<p>本书在开源社区 200 多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!</p>
<a href="https://github.com/krahets/hello-algo/graphs/contributors">
<img src="https://contrib.rocks/image?repo=krahets/hello-algo&max=300&columns=12" alt="Contributors" style="width: 100%; max-width: 38.5em;">
</a>