Skip to content

Commit 8c9a41e

Browse files
committed
修复和优化语句表述
1 parent 366bdc9 commit 8c9a41e

15 files changed

+854
-736
lines changed

docs/00_preface/00_01_preface.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,3 @@ LeetCode 等平台的算法题已成为行业通用标准,许多公司会直
4444
学习算法应当循序渐进,从基础数据结构入手,逐步掌握常见的算法思想。每当学习一个新概念,都要通过实际题目加以巩固,长期积累下来,才能建立起完善的算法知识体系。
4545

4646
本书「算法通关手册」旨在帮助读者系统学习算法知识,既包含基础理论讲解,也有大量实战题目分析。通过理论与实践相结合,读者能够真正掌握算法精髓,提升解决问题的能力。无论是备战面试,还是提升编程能力,这本书都将为你带来切实的帮助。
47-

docs/01_array/01_01_array_basic.md

Lines changed: 115 additions & 126 deletions
Large diffs are not rendered by default.

docs/01_array/01_02_array_sort.md

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,66 @@
66

77
排序算法可以按照不同的标准进行分类:
88

9-
1. 按照时间复杂度分类
10-
- 简单排序算法:时间复杂度为 $O(n^2)$,如冒泡排序、选择排序、插入排序
11-
- 高级排序算法:时间复杂度为 $O(n \log n)$,如快速排序、归并排序、堆排序
12-
- 线性排序算法:时间复杂度为 $O(n)$,如计数排序、桶排序、基数排序
9+
1. 按照时间复杂度分类
10+
- **简单排序算法**:时间复杂度为 $O(n^2)$,如冒泡排序、选择排序、插入排序
11+
- **高级排序算法**:时间复杂度为 $O(n \log n)$,如快速排序、归并排序、堆排序
12+
- **线性排序算法**:时间复杂度为 $O(n)$,如计数排序、桶排序、基数排序
1313

14-
2. 按照空间复杂度分类
15-
- 原地排序算法:空间复杂度为 $O(1)$,如冒泡排序、选择排序、插入排序、快速排序、堆排序
16-
- 非原地排序算法:空间复杂度为 $O(n)$,如归并排序、计数排序、桶排序、基数排序
14+
2. 按照空间复杂度分类
15+
- **原地排序算法**:空间复杂度为 $O(1)$,如冒泡排序、选择排序、插入排序、快速排序、堆排序
16+
- **非原地排序算法**:空间复杂度为 $O(n)$ 或更高,如归并排序、计数排序、桶排序、基数排序
1717

18-
3. 按照稳定性分类
19-
- 稳定排序算法:相等元素的相对顺序在排序后保持不变,如冒泡排序、插入排序、归并排序、计数排序、桶排序、基数排序
20-
- 不稳定排序算法:相等元素的相对顺序在排序后可能改变,如选择排序、快速排序、堆排序
18+
3. 按照稳定性分类
19+
- **稳定排序算法**:相等元素的相对顺序在排序后保持不变,如冒泡排序、插入排序、归并排序、计数排序、桶排序、基数排序
20+
- **不稳定排序算法**:相等元素的相对顺序在排序后可能改变,如选择排序、快速排序、堆排序
2121

2222
## 2. 排序算法的评价指标
2323

2424
评价一个排序算法的好坏,主要从以下几个方面考虑:
2525

26-
1. 时间复杂度:算法执行所需的时间
27-
2. 空间复杂度:算法执行所需的额外空间
28-
3. 稳定性:相等元素的相对顺序是否保持不变
29-
4. 原地性:是否需要在原数组之外开辟额外空间
30-
5. 自适应性:算法是否能够利用输入数据的特性来提高效率
26+
1. **时间复杂度**:算法执行所需的时间,包括最好情况、最坏情况和平均情况
27+
2. **空间复杂度**:算法执行所需的额外空间(不包括输入数据本身)
28+
3. **稳定性**:相等元素的相对顺序是否保持不变
29+
4. **原地性**:是否需要在原数组之外开辟额外空间
3130

3231
## 3. 常见排序算法
3332

3433
常见的排序算法包括:
3534

36-
1. 冒泡排序:通过相邻元素比较和交换,将最大元素逐步"冒泡"到数组末尾
37-
2. 选择排序:每次从未排序区间选择最小元素,放到已排序区间末尾
38-
3. 插入排序:将未排序区间的元素插入到已排序区间的合适位置
39-
4. 快速排序:选择一个基准元素,将数组分为两部分,递归排序
40-
5. 归并排序:将数组分成两半,分别排序后合并
41-
6. 堆排序:利用堆这种数据结构进行排序
42-
7. 计数排序:统计每个元素出现的次数,按顺序输出
43-
8. 桶排序:将元素分到有限数量的桶中,对每个桶单独排序
44-
9. 基数排序:按照元素的位数进行排序
35+
1. **冒泡排序**:通过相邻元素比较和交换,将最大元素逐步「冒泡」到数组末尾
36+
2. **选择排序**:每次从未排序区间选择最小元素,放到已排序区间末尾
37+
3. **插入排序**:将未排序区间的元素插入到已排序区间的合适位置
38+
4. **希尔排序**:先按一定间隔分组进行插入排序,逐步缩小间隔,最后整体进行插入排序
39+
5. **归并排序**:将数组分成两半,分别排序后合并
40+
6. **快速排序**:选择一个基准元素,将数组分为两部分,递归排序
41+
7. **堆排序**:利用堆这种数据结构进行排序
42+
8. **计数排序**:统计每个元素出现的次数,按顺序输出
43+
9. **桶排序**:将元素分到有限数量的桶中,对每个桶单独排序
44+
10. **基数排序**:按照元素的位数进行排序
4545

46-
## 4. 排序算法的选择
46+
## 4. 排序算法的选择策略
4747

4848
在实际应用中,选择合适的排序算法需要考虑以下因素:
4949

50-
1. 数据规模:小规模数据可以使用简单排序算法,大规模数据需要使用高级排序算法
51-
2. 数据特征:如果数据基本有序,插入排序效率较高;如果数据分布均匀,快速排序效率较高
52-
3. 空间限制:如果空间有限,应该选择原地排序算法
53-
4. 稳定性要求:如果需要保持相等元素的相对顺序,应该选择稳定排序算法
54-
5. 硬件环境:不同的硬件环境可能适合不同的排序算法
50+
1. 数据规模
51+
- **小规模数据**(n < 50):可以使用简单排序算法,如插入排序
52+
- **中等规模数据**(50 ≤ n < 1000):推荐使用快速排序或归并排序
53+
- **大规模数据**(n ≥ 1000):优先考虑快速排序、归并排序或堆排序
54+
2. 数据特征
55+
- **基本有序**:插入排序效率较高
56+
- **数据分布均匀**:快速排序效率较高
57+
- **数据范围较小**:计数排序或桶排序可能更高效
58+
- **数据有大量重复**:三路快速排序或计数排序更合适
59+
3. 环境约束
60+
- **空间限制**:选择原地排序算法
61+
- **稳定性要求**:选择稳定排序算法
62+
- **硬件环境**:考虑缓存友好性和并行化潜力
63+
4. 实际应用场景
64+
- **系统排序**:通常使用快速排序或归并排序的混合算法
65+
- **外部排序**:使用归并排序
66+
- **实时系统**:优先考虑时间复杂度稳定的算法
67+
- **内存受限环境**:选择空间复杂度低的算法
68+
69+
## 5. 总结
70+
71+
选择合适的排序算法需要综合考虑数据特征、环境约束和性能要求。在实际开发中,大多数编程语言的标准库都提供了经过优化的排序函数,这些函数通常结合了多种排序算法的优点,能够适应不同的数据特征。

docs/01_array/01_03_array_bubble_sort.md

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@
22

33
> **冒泡排序(Bubble Sort)基本思想**
44
>
5-
> 经过多次迭代,通过相邻元素之间的比较与交换,使值较小的元素逐步从后面移到前面,值较大的元素从前面移到后面
5+
> 通过相邻元素的比较与交换,将较大的元素逐步「冒泡」到数组末尾,较小的元素自然「下沉」到数组开头
66
7-
这个过程就像水底的气泡一样从底部向上「冒泡」到水面,这也是冒泡排序法名字的由来
7+
冒泡排序的名字来源于这个过程:就像水中的气泡从底部向上浮到水面一样,较大的元素会逐步移动到数组的末尾
88

9-
接下来,我们使用「冒泡」的方式来模拟一下这个过程。
10-
11-
1. 首先将数组想象是一排「泡泡」,元素值的大小与泡泡的大小成正比。
12-
2. 然后从左到右依次比较相邻的两个「泡泡」:
13-
1. 如果左侧泡泡大于右侧泡泡,则交换两个泡泡的位置。
14-
2. 如果左侧泡泡小于等于右侧泡泡,则两个泡泡保持不变。
15-
3. 这 $1$ 趟遍历完成之后,最大的泡泡就会放置到所有泡泡的最右侧,就像是「泡泡」从水底向上浮到了水面。
9+
**我们使用「冒泡」的方式来模拟一下这个过程**
10+
1. 将数组元素想象成大小不同的「泡泡」,值越大的元素「泡泡」越大。
11+
2. 从左到右依次比较相邻的两个元素。
12+
3. 如果左侧元素大于右侧元素,则交换位置。
13+
4. 每完成一趟遍历,最大的元素就会「浮」到最右侧。
1614

1715
::: tabs#bubble
1816

@@ -48,64 +46,70 @@
4846

4947
## 2. 冒泡排序算法步骤
5048

51-
假设数组的元素个数为 $n$ 个,则冒泡排序的算法步骤如下
49+
对于长度为 $n$ 的数组,冒泡排序的步骤如下
5250

53-
1. 第 $1$ 趟「冒泡」:对前 $n$ 个元素执行「冒泡」,从而使第 $1$ 个值最大的元素放置在正确位置上
54-
1. 先将序列中第 $1$ 个元素与第 $2$ 个元素进行比较,如果前者大于后者,则两者交换位置,否则不交换
55-
2. 然后将第 $2$ 个元素与第 $3$ 个元素比较,如果前者大于后者,则两者交换位置,否则不交换
56-
3. 依次类推,直到第 $n - 1$ 个元素与第 $n$ 个元素比较(或交换)为止
57-
4. 经过第 $1$ 趟排序,使得 $n$ 个元素中第 $i$ 个值最大元素被安置在第 $n$ 个位置上
58-
2. 第 $2$ 趟「冒泡」:对前 $n - 1$ 个元素执行「冒泡」,从而使第 $2$ 个值最大的元素放置在正确位置上
59-
1. 先将序列中第 $1$ 个元素与第 $2$ 个元素进行比较,若前者大于后者,则两者交换位置,否则不交换
60-
2. 然后将第 $2$ 个元素与第 $3$ 个元素比较,若前者大于后者,则两者交换位置,否则不交换
61-
3. 依次类推,直到对 $n - 2$ 个元素与第 $n - 1$ 个元素比较(或交换)为止
62-
4. 经过第 $2$ 趟排序,使得数组中第 $2$ 个值最大元素被安置在第 $n$ 个位置上
63-
3. 依次类推,重复上述「冒泡」过程,直到某一趟排序过程中不出现元素交换位置的动作,则排序结束
51+
1. 第 $1$ 趟冒泡:对前 $n$ 个元素依次比较相邻元素,将较大的元素向右交换,最终使最大值移动到数组末尾(第 $n$ 个位置)
52+
1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换
53+
2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换
54+
3. 以此类推,直到比较第 $n - 1$ 个和第 $n$ 个元素
55+
4. 完成后,最大元素已位于末尾
56+
2. 第 $2$ 趟冒泡:对前 $n-1$ 个元素重复上述过程,将次大值移动到倒数第二个位置(第 $n-1$ 个位置)
57+
1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换
58+
2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换
59+
3. 以此类推,直到比较第 $n-2$ 个和第 $n-1$ 个元素
60+
4. 完成后,次大元素已位于倒数第二位
61+
3. 持续进行上述冒泡过程,每一趟比较的元素个数递减,直到某一趟未发生任何交换,说明数组已完全有序,排序结束
6462

65-
我们以 $[5, 2, 3, 6, 1, 4]$ 为例,演示一下冒泡排序算法的整个步骤
63+
以数组 $[5, 2, 3, 6, 1, 4]$ 为例,演示一下冒泡排序的算法步骤
6664

67-
![冒泡排序算法步骤](https://qcdn.itcharge.cn/images/20230816154510.png)
65+
![冒泡排序的算法步骤](https://qcdn.itcharge.cn/images/20230816154510.png)
6866

6967
## 3. 冒泡排序代码实现
7068

7169
```python
7270
class Solution:
7371
def bubbleSort(self, nums: [int]) -> [int]:
74-
# 第 i 趟「冒泡」
75-
for i in range(len(nums) - 1):
76-
flag = False # 是否发生交换的标志位
77-
# 从数组中前 n - i + 1 个元素的第 1 个元素开始,相邻两个元素进行比较
78-
for j in range(len(nums) - i - 1):
79-
# 相邻两个元素进行比较,如果前者大于后者,则交换位置
72+
"""冒泡排序算法实现"""
73+
n = len(nums)
74+
# 外层循环控制趟数,每一趟将当前未排序区间的最大值“冒泡”到末尾
75+
for i in range(n - 1):
76+
swapped = False # 记录本趟是否发生过交换
77+
# 内层循环负责相邻元素两两比较,将较大值后移
78+
for j in range(n - i - 1):
79+
# 如果前一个元素大于后一个元素,则交换
8080
if nums[j] > nums[j + 1]:
8181
nums[j], nums[j + 1] = nums[j + 1], nums[j]
82-
flag = True
83-
if not flag: # 此趟遍历未交换任何元素,直接跳出
82+
swapped = True # 发生了交换
83+
# 如果本趟没有发生任何交换,说明数组已经有序,可以提前结束
84+
if not swapped:
8485
break
85-
86-
return nums
87-
86+
return nums # 返回排序后的数组
87+
8888
def sortArray(self, nums: [int]) -> [int]:
89+
"""排序数组的接口,调用冒泡排序"""
8990
return self.bubbleSort(nums)
9091
```
9192

9293
## 4. 冒泡排序算法分析
9394

94-
- **最佳时间复杂度**:$O(n)$。最好的情况下(初始时序列已经是升序排列),只需经过 $1$ 趟排序,总共经过 $n$ 次元素之间的比较,并且不移动元素,算法就可以结束排序。因此,冒泡排序算法的最佳时间复杂度为 $O(n)$。
95-
- **最坏时间复杂度**:$O(n^2)$。最差的情况下(初始时序列已经是降序排列,或者最小值元素处在序列的最后),则需要进行 $n$ 趟排序,总共进行 $∑^n_{i=2}(i−1) = \frac{n(n−1)}{2}$ 次元素之间的比较,因此,冒泡排序算法的最坏时间复杂度为 $O(n^2)$。
96-
- **空间复杂度**:$O(1)$。冒泡排序为原地排序算法,只用到指针变量 $i$、$j$ 以及标志位 $flag$ 等常数项的变量。
97-
- **冒泡排序适用情况**:冒泡排序方法在排序过程中需要移动较多次数的元素,并且排序时间效率比较低。因此,冒泡排序方法比较适合于参加排序序列的数据量较小的情况,尤其是当序列的初始状态为基本有序的情况。
98-
- **排序稳定性**:由于元素交换是在相邻元素之间进行的,不会改变相等元素的相对顺序,因此,冒泡排序法是一种 **稳定排序算法**
95+
| 指标 | 复杂度 | 说明 |
96+
|------|--------|------|
97+
| **最佳时间复杂度** | $O(n)$ | 数组已有序,只需一趟遍历 |
98+
| **最坏时间复杂度** | $O(n^2)$ | 数组逆序,需要 $n$ 趟遍历 |
99+
| **平均时间复杂度** | $O(n^2)$ | 一般情况下的复杂度 |
100+
| **空间复杂度** | $O(1)$ | 原地排序,只使用常数空间 |
101+
| **稳定性** | ✅ 稳定 | 相等元素相对位置不变 |
99102

100-
## 5. 总结
103+
**适用场景**
104+
- 数据量较小($n < 50$)
105+
- 数据基本有序
101106

102-
冒泡排序是一种简单的排序算法。它通过多次比较相邻元素并交换位置,将较大的元素逐步移动到数组末尾。
103-
104-
冒泡排序的时间复杂度取决于数据情况。最好情况下是 $O(n)$,最坏情况下是 $O(n^2)$。它只需要常数级别的额外空间,空间复杂度是 $O(1)$。
107+
## 5. 总结
105108

106-
冒泡排序适合数据量小的场景。当数据基本有序时,它的效率较高。由于交换只在相邻元素间进行,冒泡排序是稳定的排序算法。
109+
冒泡排序是最简单的排序算法之一,通过相邻元素比较交换实现排序。虽然实现简单,但效率较低。
107110

108-
冒泡排序容易实现,但效率较低。在实际应用中,通常选择更高效的排序算法处理大规模数据。
111+
**优点**:实现简单,稳定排序,空间复杂度低
112+
**缺点**:时间复杂度高,交换次数多
109113

110114
## 练习题目
111115

0 commit comments

Comments
 (0)