Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@

### 1.3 使用说明

- 本电子书的左侧为所有章节目录导航,可直接点击对应章节跳转阅读
- 本电子书左上角有搜索栏,可以帮你迅速找到想看的章节和题解文章
- 本电子书每页都接入了 giscus 评论系统,可在每页下方的评论框进行评论(需使用 GitHub 账号登录)。
- 建议按照章节顺序学习,循序渐进地掌握各个知识点
- 每章末尾都配有练习题,建议及时完成以巩固所学知识
- 本电子书左侧提供了完整的章节目录导航,可直接点击跳转至相应内容
- 本电子书右上角配有搜索栏,便于快速查找所需章节和题解文章
- 本电子书集成了 giscus 评论系统,欢迎在页面底部评论区留言(需 GitHub 账号登录)。
- 建议按章节顺序系统学习,逐步掌握各知识点;也可根据兴趣自由选择章节阅读
- 每篇内容末尾设有练习题,建议及时完成以加深理解、巩固所学

## 2. 相关说明

### 2.1 关于作者

我是一名 iOS / macOS 的开发程序员,研究生毕业于北航软件学院。曾在大学期间学习过算法知识,并参加过 3 年的 ACM 比赛, 但水平有限,未能取得理想成绩。但是这 3 年的 ACM 经历,给我最大的收获是锻炼了自己的逻辑思维和解决实际问题的能力,这种能力为我今后的工作、学习打下了坚实的基础。

我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 800+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。
我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到目前为止日已经刷了 1000+ 道题目,并且完成了 800+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。

### 2.2 互助与勘误

Expand Down
6 changes: 3 additions & 3 deletions docs/00_preface/00_01_preface.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@

在这个过程中,我深刻体会到一个重要秘诀:**「输出」是最有效的学习方式**,这正是费曼学习法的真实写照。

只有真正理解了某个概念,才能用简明易懂的语言表达出来,让他人也能明白。如果自己尚未吃透,就很难讲清楚。为此,我大量阅读算法书籍和优质博客,反复思考,直到能够将复杂的内容转化为通俗的文字
只有真正理解了某个概念,才能用简明易懂的语言表达出来,让他人也能明白。如果自己尚未吃透,就很难讲清楚。为此,我广泛阅读了各类算法书籍和高质量博客,不断思考和总结,力求把复杂的知识用更简单通俗的方式表达出来

在刷题过程中,许多朋友和群友与我交流算法知识,指出不足,提出建议。这些宝贵的反馈如同专业老师批改作业,不仅帮助我完善内容,也加深了对算法的理解
刷题过程中,我与许多朋友和群里的小伙伴们积极探讨算法知识,大家互相交流、分享见解,也会帮助我发现不足并提出改进建议。这些宝贵反馈如同专业老师批改作业,不仅不断推动内容的完善,也让我对算法有了更加深入的理解

就这样,从 2021 年 7 月到 2022 年 7 月,经过一年的坚持,我在 LeetCode 上完成了 1000 多道题目,系统总结了算法与数据结构知识,最终完成了这本 **「算法通关手册」**。
就这样,从 2021 年 7 月到 2022 年 7 月,经过一年的坚持,我在 LeetCode 上完成了 1000 多道题目,然后系统总结了算法与数据结构知识,最终完成了这本 **「算法通关手册」**。

## 2. 为什么要学习算法和数据结构

Expand Down
11 changes: 8 additions & 3 deletions docs/00_preface/00_02_data_structures_algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@

根据数据元素之间的关系,数据的逻辑结构通常分为以下四类:

1. 集合结构
2. 线性结构
3. 树形结构
4. 图形结构

#### 1.1.1 集合结构

> **集合结构**:数据元素属于同一个集合,彼此之间没有其他关系。
Expand Down Expand Up @@ -152,18 +157,18 @@

1. **输入**:算法需要接收外部提供的信息作为处理对象,这些信息称为输入。一个算法可以有零个、一个或多个输入。例如,示例 $1$ 的输入是出发地和目的地(如上海、北京),示例 $3$ 的输入是由 $n$ 个整数构成的数组,而示例 $2$ 针对的是固定问题,可以视为没有输入。
2. **输出**:算法的执行结果必须有明确的输出,即至少有一个输出结果。比如,示例 $1$ 的输出是最终选择的交通方式,示例 $2$ 的输出是求和的结果,示例 $3$ 的输出是排好序的数组。
3. **有穷性**:算法必须在有限的步骤内终止,并且能够在合理的时间内完成。如果算法无法在有限时间内结束,就不能称为有效的算法。例如,若五一假期从上海到北京旅游,三天都没决定交通方式,计划就无法实现,这样的「算法」显然不合理。
3. **有穷性**:算法必须在有限的步骤内终止,并且能够在合理的时间内完成。如果算法无法在有限时间内结束,就不能称为有效的算法。例如,如果五一假期从上海到北京旅游,三天都没决定交通方式,计划就无法实现,这样的「算法」显然不合理。
4. **确定性**:算法中的每一步操作都必须有明确、唯一的含义,不能存在歧义。也就是说,任何人在相同输入下执行算法,得到的中间过程和最终结果都应一致。
5. **可行性**:算法的每一步都必须是可执行的,即在现有条件下能够通过有限次数的操作实现,并且可以被计算机程序实现并运行,最终得到正确的结果。

### 2.2 算法追求的目标

研究算法的核心目的,是让我们以更高效的方式解决问题。对于同一个问题,往往存在多种算法可选,而不同算法的“代价”也各不相同。一般来说,优秀的算法应当重点追求以下两个目标:
研究算法的核心目的,是让我们以更高效的方式解决问题。对于同一个问题,往往存在多种算法可选,而不同算法的「代价」也各不相同。一般来说,优秀的算法应当重点追求以下两个目标:

1. **更少的运行时间(更低的时间复杂度)**
2. **更小的内存占用(更低的空间复杂度)**

举例来说,假设计算机执行一条指令需要 $1$ 纳秒。若某算法需 $100$ 纳秒,另一算法只需 $3$ 纳秒,在不考虑内存消耗的前提下,显然后者更优。再比如,若某算法只需 $3$ 字节内存,另一算法需 $100$ 字节,在不考虑运行时间的情况下,前者更优。
举例来说,假设计算机执行一条指令需要 $1$ 纳秒。如果某算法需 $100$ 纳秒,另一算法只需 $3$ 纳秒,在不考虑内存消耗的前提下,显然后者更优。再比如,如果某算法只需 $3$ 字节内存,另一算法需 $100$ 字节,在不考虑运行时间的情况下,前者更优。

实际应用中,算法设计往往需要在运行时间和空间占用之间权衡。理想情况下,算法既快又省空间,但现实中常常需要根据具体需求做出取舍。例如,当程序运行速度要求较高时,可以适当增加空间消耗以换取更快的执行速度;反之,如果设备内存有限且对速度要求不高,则可以选择更节省空间的算法,即使牺牲一些运行时间。

Expand Down
22 changes: 11 additions & 11 deletions docs/00_preface/00_03_algorithm_complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def find_max(arr):

> **渐近上界符号 $O$**:用于描述算法运行时间的上限,通常反映算法在最坏情况下的性能。

**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,若存在正常数 $c$ 和 $n_0$,使得对所有 $n \geq n_0$,都有 $T(n) \leq c \cdot f(n)$,则称 $T(n) = O(f(n))$。
**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,如果存在正常数 $c$ 和 $n_0$,使得对所有 $n \geq n_0$,都有 $T(n) \leq c \cdot f(n)$,则称 $T(n) = O(f(n))$。

**直观理解**:$T(n) = O(f(n))$ 表示「算法的运行时间至多为 $f(n)$ 的某个常数倍」,即不会比 $f(n)$ 增长得更快。

Expand All @@ -79,7 +79,7 @@ def find_max(arr):

> **渐近下界符号 $\Omega$**:用于描述算法运行时间的下界,通常反映算法在最优情况下的性能。

**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,若存在正常数 $c > 0$ 和 $n_0$,使得对所有 $n \geq n_0$,都有 $T(n) \geq c \cdot f(n)$,则称 $T(n) = \Omega(f(n))$。
**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,如果存在正常数 $c > 0$ 和 $n_0$,使得对所有 $n \geq n_0$,都有 $T(n) \geq c \cdot f(n)$,则称 $T(n) = \Omega(f(n))$。

**直观理解**:$T(n) = \Omega(f(n))$ 表示「算法的运行时间至少不会低于 $f(n)$ 的某个常数倍」,即增长速度不慢于 $f(n)$。

Expand All @@ -93,7 +93,7 @@ def find_max(arr):

> **渐近紧确界符号 $\Theta$**:用于描述算法运行时间的精确数量级,即算法在最好和最坏情况下的增长速度都与 $f(n)$ 保持一致。

**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,若存在正常数 $c_1, c_2 > 0$ 及 $n_0$,使得对所有 $n \geq n_0$,都有 $c_1 \cdot f(n) \leq T(n) \leq c_2 \cdot f(n)$,则称 $T(n) = \Theta(f(n))$。
**数学定义**:设 $T(n)$ 和 $f(n)$ 为两个函数,如果存在正常数 $c_1, c_2 > 0$ 及 $n_0$,使得对所有 $n \geq n_0$,都有 $c_1 \cdot f(n) \leq T(n) \leq c_2 \cdot f(n)$,则称 $T(n) = \Theta(f(n))$。

**直观理解**:$T(n) = \Theta(f(n))$ 表示「算法运行时间与 $f(n)$ 同阶」,即上下界都为 $f(n)$ 的常数倍。

Expand Down Expand Up @@ -303,15 +303,15 @@ def generate_permutations(arr):

常见时间复杂度从小到大排序:$O(1)$ < $O(\log n)$ < $O(n)$ < $O(n \log n)$ < $O(n^2)$ < $O(n^3)$ < $O(2^n)$ < $O(n!)$ < $O(n^n)$

| 时间复杂度 | 输入规模 n=10 | n=100 | n=1000 | 实际应用 |
| 时间复杂度 | 输入规模 $n=10$ | $n=100$ | $n=1000$ | 实际应用 |
|------------|---------------|-------|--------|----------|
| $O(1)$ | 1 | 1 | 1 | 数组访问、哈希表查找 |
| $O(\log n)$| 3 | 7 | 10 | 二分查找、平衡树操作 |
| $O(n)$ | 10 | 100 | 1000 | 线性搜索、数组遍历 |
| $O(n \log n)$ | 33 | 664 | 9966 | 快速排序、归并排序 |
| $O(n^2)$ | 100 | 10000 | 1000000| 冒泡排序、选择排序 |
| $O(2^n)$ | 1024 | $1.3 \times 10^30$ | $1.1 \times 10^301$ | 递归斐波那契 |
| $O(n!)$ | 3628800 | $9.3 \times 10^157$ | $4.0 \times 10^2567$ | 全排列 |
| $O(1)$ | $1$ | $1$ | $1$ | 数组访问、哈希表查找 |
| $O(\log n)$| $3$ | $7$ | $10$ | 二分查找、平衡树操作 |
| $O(n)$ | $10$ | $100$ | $1000$ | 线性搜索、数组遍历 |
| $O(n \log n)$ | $33$ | $664$ | $9966$ | 快速排序、归并排序 |
| $O(n^2)$ | $100$ | $10000$ | $1000000$ | 冒泡排序、选择排序 |
| $O(2^n)$ | $1024$ | $1.3 \times 10^{30}$ | $1.1 \times 10^{301}$ | 递归斐波那契 |
| $O(n!)$ | $3628800$ | $9.3 \times 10^{157}$ | $4.0 \times 10^{2567}$ | 全排列 |

### 2.4 最佳、最坏、平均时间复杂度

Expand Down
2 changes: 1 addition & 1 deletion docs/00_preface/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
愿本书与你同行,助你轻装前行,照亮属于你的算法之路。
:::

# 本章内容
## 本章内容

- [0.1 前言](https://github.com/ITCharge/AlgoNote/tree/main/docs/00_preface/00_01_preface.md)
- [0.2 算法与数据结构](https://github.com/ITCharge/AlgoNote/tree/main/docs/00_preface/00_02_data_structures_algorithms.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/01_array/01_02_array_sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@

## 5. 总结

排序算法的核心目标是将数据按指定顺序排列。常见排序算法各有优缺点,选择时需结合数据规模、数据特性和实际需求。一般来说,小规模数据可用插入、冒泡等简单算法;大规模或高性能场景优先考虑快速排序、归并排序等高效算法。若有稳定性或空间限制等特殊要求,应优先选择满足条件的算法。理解各种排序的原理和适用场景,有助于在实际开发中做出最优选择。
排序算法的核心目标是将数据按指定顺序排列。常见排序算法各有优缺点,选择时需结合数据规模、数据特性和实际需求。一般来说,小规模数据可用插入、冒泡等简单算法;大规模或高性能场景优先考虑快速排序、归并排序等高效算法。如果有稳定性或空间限制等特殊要求,应优先选择满足条件的算法。理解各种排序的原理和适用场景,有助于在实际开发中做出最优选择。

10 changes: 5 additions & 5 deletions docs/01_array/01_03_array_bubble_sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@
对于长度为 $n$ 的数组,冒泡排序的步骤如下:

1. 第 $1$ 趟冒泡:对前 $n$ 个元素依次比较相邻元素,将较大的元素向右交换,最终使最大值移动到数组末尾(第 $n$ 个位置)。
1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换
2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换
1. 比较第 $1$ 个和第 $2$ 个元素,如果前者大于后者则交换
2. 比较第 $2$ 个和第 $3$ 个元素,如果前者大于后者则交换
3. 以此类推,直到比较第 $n - 1$ 个和第 $n$ 个元素。
4. 完成后,最大元素已位于末尾。
2. 第 $2$ 趟冒泡:对前 $n-1$ 个元素重复上述过程,将次大值移动到倒数第二个位置(第 $n-1$ 个位置)。
1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换
2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换
1. 比较第 $1$ 个和第 $2$ 个元素,如果前者大于后者则交换
2. 比较第 $2$ 个和第 $3$ 个元素,如果前者大于后者则交换
3. 以此类推,直到比较第 $n-2$ 个和第 $n-1$ 个元素。
4. 完成后,次大元素已位于倒数第二位。
3. 持续进行上述冒泡过程,每一趟比较的元素个数递减,直到某一趟未发生任何交换,说明数组已完全有序,排序结束。
Expand All @@ -72,7 +72,7 @@ class Solution:
def bubbleSort(self, nums: [int]) -> [int]:
"""冒泡排序算法实现"""
n = len(nums)
# 外层循环控制趟数,每一趟将当前未排序区间的最大值“冒泡”到末尾
# 外层循环控制趟数,每一趟将当前未排序区间的最大值「冒泡」到末尾
for i in range(n - 1):
swapped = False # 记录本趟是否发生过交换
# 内层循环负责相邻元素两两比较,将较大值后移
Expand Down
4 changes: 2 additions & 2 deletions docs/01_array/01_06_array_shell_sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Solution:
|------|--------|------|
| **最佳时间复杂度** | $O(n)$ | 当数组已有序时 |
| **最坏时间复杂度** | $O(n^2)$ | 使用普通间隔序列时 |
| **平均时间复杂度** | $O(n^{1.3})$ ~ $O(n^{1.5})$ | 取决于间隔序列选择,若选取得当接近于 $O(n \log n)$ |
| **平均时间复杂度** | $O(n^{1.3})$ ~ $O(n^{1.5})$ | 取决于间隔序列选择,如果选取得当接近于 $O(n \log n)$ |
| **空间复杂度** | $O(1)$ | 原地排序,只使用常数空间 |
| **稳定性** | 不稳定 | 不同组间的相等元素可能改变相对顺序 |

Expand All @@ -91,7 +91,7 @@ class Solution:
- 希尔排序的时间复杂度高度依赖于间隔序列的选择。
- 当采用常见的 `gap = gap // 2` 间隔序列时,排序过程大约需要 $\log_2 n$ 趟,每一趟的操作类似于分组插入排序。
- 每一趟的排序时间复杂度约为 $O(n)$,但随着 gap 的减小,实际操作次数逐步减少。
- 综合来看,希尔排序的整体时间复杂度通常介于 $O(n \log n)$ 和 $O(n^2)$ 之间,若间隔序列选择得当,性能可接近 $O(n \log n)$。
- 综合来看,希尔排序的整体时间复杂度通常介于 $O(n \log n)$ 和 $O(n^2)$ 之间,如果间隔序列选择得当,性能可接近 $O(n \log n)$。

**适用场景**:

Expand Down
42 changes: 21 additions & 21 deletions docs/01_array/01_09_array_heap_sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

在实际编程中,堆通常采用数组进行存储。使用数组表示堆时,节点与数组索引之间的对应关系如下:

- 若某节点的下标为 $i$,则其左孩子的下标为 $2 \times i + 1$,右孩子的下标为 $2 \times i + 2$;
- 若某节点的下标为 $i$,则其父节点的下标为 $\lfloor \frac{i - 1}{2} \rfloor$。
- 如果某节点的下标为 $i$,则其左孩子的下标为 $2 \times i + 1$,右孩子的下标为 $2 \times i + 2$;
- 如果某节点的下标为 $i$,则其父节点的下标为 $\lfloor \frac{i - 1}{2} \rfloor$。

如下图所示,顺序存储结构(数组)可以高效地表示堆:

Expand Down Expand Up @@ -245,31 +245,31 @@ def __shift_down(self, i: int, n: int):

@tab <1>

![1. 构建初始大顶堆 1](https://qcdn.itcharge.cn/images/20230831151620.png)
![构建初始大顶堆 1](https://qcdn.itcharge.cn/images/20230831151620.png)

@tab <2>

![1. 构建初始大顶堆 2](https://qcdn.itcharge.cn/images/20230831151641.png)
![构建初始大顶堆 2](https://qcdn.itcharge.cn/images/20230831151641.png)

@tab <3>

![1. 构建初始大顶堆 3](https://qcdn.itcharge.cn/images/20230831151703.png)
![构建初始大顶堆 3](https://qcdn.itcharge.cn/images/20230831151703.png)

@tab <4>

![1. 构建初始大顶堆 4](https://qcdn.itcharge.cn/images/20230831151715.png)
![构建初始大顶堆 4](https://qcdn.itcharge.cn/images/20230831151715.png)

@tab <5>

![1. 构建初始大顶堆 5](https://qcdn.itcharge.cn/images/20230831151725.png)
![构建初始大顶堆 5](https://qcdn.itcharge.cn/images/20230831151725.png)

@tab <6>

![1. 构建初始大顶堆 6](https://qcdn.itcharge.cn/images/20230831151735.png)
![构建初始大顶堆 6](https://qcdn.itcharge.cn/images/20230831151735.png)

@tab <7>

![1. 构建初始大顶堆 7](https://qcdn.itcharge.cn/images/20230831151749.png)
![构建初始大顶堆 7](https://qcdn.itcharge.cn/images/20230831151749.png)

:::

Expand All @@ -284,51 +284,51 @@ def __shift_down(self, i: int, n: int):

@tab <1>

![2. 交换元素,调整堆 1](https://qcdn.itcharge.cn/images/20230831162335.png)
![交换元素,调整堆 1](https://qcdn.itcharge.cn/images/20230831162335.png)

@tab <2>

![2. 交换元素,调整堆 2](https://qcdn.itcharge.cn/images/20230831162346.png)
![交换元素,调整堆 2](https://qcdn.itcharge.cn/images/20230831162346.png)

@tab <3>

![2. 交换元素,调整堆 3](https://qcdn.itcharge.cn/images/20230831162359.png)
![交换元素,调整堆 3](https://qcdn.itcharge.cn/images/20230831162359.png)

@tab <4>

![2. 交换元素,调整堆 4](https://qcdn.itcharge.cn/images/20230831162408.png)
![交换元素,调整堆 4](https://qcdn.itcharge.cn/images/20230831162408.png)

@tab <5>

![2. 交换元素,调整堆 5](https://qcdn.itcharge.cn/images/20230831162416.png)
![交换元素,调整堆 5](https://qcdn.itcharge.cn/images/20230831162416.png)

@tab <6>

![2. 交换元素,调整堆 6](https://qcdn.itcharge.cn/images/20230831162424.png)
![交换元素,调整堆 6](https://qcdn.itcharge.cn/images/20230831162424.png)

@tab <7>

![2. 交换元素,调整堆 7](https://qcdn.itcharge.cn/images/20230831162431.png)
![交换元素,调整堆 7](https://qcdn.itcharge.cn/images/20230831162431.png)

@tab <8>

![2. 交换元素,调整堆 8](https://qcdn.itcharge.cn/images/20230831162440.png)
![交换元素,调整堆 8](https://qcdn.itcharge.cn/images/20230831162440.png)

@tab <9>

![2. 交换元素,调整堆 9](https://qcdn.itcharge.cn/images/20230831162449.png)
![交换元素,调整堆 9](https://qcdn.itcharge.cn/images/20230831162449.png)

@tab <10>

![2. 交换元素,调整堆 10](https://qcdn.itcharge.cn/images/20230831162457.png)
![交换元素,调整堆 10](https://qcdn.itcharge.cn/images/20230831162457.png)

@tab <11>

![2. 交换元素,调整堆 11](https://qcdn.itcharge.cn/images/20230831162505.png)
![交换元素,调整堆 11](https://qcdn.itcharge.cn/images/20230831162505.png)

@tab <12>

![2. 交换元素,调整堆 12](https://qcdn.itcharge.cn/images/20230831162512.png)
![交换元素,调整堆 12](https://qcdn.itcharge.cn/images/20230831162512.png)

:::

Expand Down
Loading