Skip to content

Commit

Permalink
更新 Latex 公式格式
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Apr 30, 2024
1 parent 2921a0e commit a9fb47a
Show file tree
Hide file tree
Showing 77 changed files with 352 additions and 352 deletions.
6 changes: 3 additions & 3 deletions docs/ch01/01.01/01.01.02-Algorithm-Complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def algorithm(n):
return fact
```

把上述算法中所有语句的执行次数加起来 $1 + n + n + 1 = 2n + 2$,可以用一个函数 $f(n)$ 来表达语句的执行次数:$f(n) = 2n + 2$。
把上述算法中所有语句的执行次数加起来 $1 + n + n + 1 = 2 \times n + 2$,可以用一个函数 $f(n)$ 来表达语句的执行次数:$f(n) = 2n + 2$。

则时间复杂度的函数可以表示为:$T(n) = O(f(n))$。它表示的是随着问题规模 n 的增大,算法执行时间的增长趋势跟 $f(n)$ 相同。$O$ 是一种渐进符号,$T(n)$ 称作算法的 **渐进时间复杂度(Asymptotic Time Complexity)**,简称为 **时间复杂度**

Expand All @@ -69,7 +69,7 @@ def algorithm(n):
也就是说,如果函数 $f(n) = \Theta(g(n))$,那么我们能找到两个正数 $c_1$、$c_2$,使得 $f(n)$ 被 $c_1 \cdot g(n)$ 和 $c_2 \cdot g(n)$ 夹在中间。

例如:$T(n) = 3n^2 + 4n + 5 = \Theta(n^2)$,可以找到 $c_1 = 1$,$c_2 = 12$,$n_0 = 1$,使得对于所有 $n \ge 1$,都有 $n^2 \le 3n^2 + 4n + 5 \le 12n^2$。
例如:$T(n) = 3 \times n^2 + 4 \times n + 5 = \Theta(n^2)$,可以找到 $c_1 = 1$,$c_2 = 12$,$n_0 = 1$,使得对于所有 $n \ge 1$,都有 $n^2 \le 3 \times n^2 + 4 \times n + 5 \le 12 \times n^2$。

#### 2.2.2 $O$ 渐进上界符号

Expand Down Expand Up @@ -187,7 +187,7 @@ def algorithm(n):
return cnt
```

上述代码中 `cnt = 1` 的时间复杂度为 $O(1)$ 可以忽略不算。`while` 循环体中 $cnt$ 从 $1$ 开始,每循环一次都乘以 $2$。当大于等于 $n$ 时循环结束。变量 $cnt$ 的取值是一个等比数列:$2^02^12^2,…,2^x$,根据 $2^x = n$,可以得出这段循环体的执行次数为 $\log_2n$,所以这段代码的时间复杂度为 $O(\log_2n)$。
上述代码中 `cnt = 1` 的时间复杂度为 $O(1)$ 可以忽略不算。`while` 循环体中 $cnt$ 从 $1$ 开始,每循环一次都乘以 $2$。当大于等于 $n$ 时循环结束。变量 $cnt$ 的取值是一个等比数列:$2^0, 2^1, 2^2, …, 2^x$,根据 $2^x = n$,可以得出这段循环体的执行次数为 $\log_2n$,所以这段代码的时间复杂度为 $O(\log_2n)$。

因为 $\log n = k \times \log_2 n$,这里 $k = 3.322$,所以,$\log n$ 与 $\log_2 n$ 的差别比较小。为了方便书写,通常我们将对数时间复杂度写作是 $O(\log n)$。

Expand Down
10 changes: 5 additions & 5 deletions docs/ch01/01.03/01.03.02-Array-Selection-Sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

1. 初始状态下,无已排序区间,未排序区间为 $[0, n - 1]$。
2. 第 $1$ 趟选择:
1. 遍历未排序区间 $[0, n - 1]$,使用变量 $min\underline{}i$ 记录区间中值最小的元素位置。
2. 将 $min\underline{}i$ 与下标为 $0$ 处的元素交换位置。如果下标为 $0$ 处元素就是值最小的元素位置,则不用交换。
1. 遍历未排序区间 $[0, n - 1]$,使用变量 $min\underline{\hspace{0.5em}}i$ 记录区间中值最小的元素位置。
2. 将 $min\underline{\hspace{0.5em}}i$ 与下标为 $0$ 处的元素交换位置。如果下标为 $0$ 处元素就是值最小的元素位置,则不用交换。
3. 此时,$[0, 0]$ 为已排序区间,$[1, n - 1]$(总共 $n - 1$ 个元素)为未排序区间。
3. 第 $2$ 趟选择:
1. 遍历未排序区间 $[1, n - 1]$,使用变量 $min\underline{}i$ 记录区间中值最小的元素位置。
2. 将 $min\underline{}i$ 与下标为 $1$ 处的元素交换位置。如果下标为 $1$ 处元素就是值最小的元素位置,则不用交换。
1. 遍历未排序区间 $[1, n - 1]$,使用变量 $min\underline{\hspace{0.5em}}i$ 记录区间中值最小的元素位置。
2. 将 $min\underline{\hspace{0.5em}}i$ 与下标为 $1$ 处的元素交换位置。如果下标为 $1$ 处元素就是值最小的元素位置,则不用交换。
3. 此时,$[0, 1]$ 为已排序区间,$[2, n - 1]$(总共 $n - 2$ 个元素)为未排序区间。
4. 依次类推,对剩余未排序区间重复上述选择过程,直到所有元素都划分到已排序区间,排序结束。

Expand Down Expand Up @@ -83,6 +83,6 @@ print(Solution().sortArray([5, 2, 3, 6, 1, 4]))

- **时间复杂度**:$O(n^2)$。排序法所进行的元素之间的比较次数与序列的原始状态无关,时间复杂度总是 $O(n^2)$。
- 这是因为无论序列中元素的初始排列状态如何,第 $i$ 趟排序要找出值最小元素都需要进行 $n − i$ 次元素之间的比较。因此,整个排序过程需要进行的元素之间的比较次数都相同,为 $∑^n_{i=2}(i - 1) = \frac{n(n−1)}{2}$ 次。
- **空间复杂度**:$O(1)$。选择排序算法为原地排序算法,只用到指针变量 $i$、$j$ 以及最小值位置 $min\underline{}i$ 等常数项的变量。
- **空间复杂度**:$O(1)$。选择排序算法为原地排序算法,只用到指针变量 $i$、$j$ 以及最小值位置 $min\underline{\hspace{0.5em}}i$ 等常数项的变量。
- **选择排序适用情况**:选择排序方法在排序过程中需要移动较多次数的元素,并且排序时间效率比较低。因此,选择排序方法比较适合于参加排序序列的数据量较小的情况。选择排序的主要优点是仅需要原地操作无需占用其他空间就可以完成排序,因此在空间复杂度要求较高时,可以考虑选择排序。
- **排序稳定性**:由于值最小元素与未排序区间第 $1$ 个元素的交换动作是在不相邻的元素之间进行的,因此很有可能会改变相等元素的相对顺序,因此,选择排序法是一种 **不稳定排序算法**
6 changes: 3 additions & 3 deletions docs/ch01/01.03/01.03.05-Array-Merge-Sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
假设数组的元素个数为 $n$ 个,则归并排序的算法步骤如下:

1. **分解过程**:先递归地将当前数组平均分成两半,直到子数组长度为 $1$。
1. 找到数组中心位置 $mid$,从中心位置将数组分成左右两个子数组 $left\underline{}nums$、$right\underline{}nums$。
2. 对左右两个子数组 $left\underline{}nums$、$right\underline{}nums$ 分别进行递归分解。
1. 找到数组中心位置 $mid$,从中心位置将数组分成左右两个子数组 $left\underline{\hspace{0.5em}}nums$、$right\underline{\hspace{0.5em}}nums$。
2. 对左右两个子数组 $left\underline{\hspace{0.5em}}nums$、$right\underline{\hspace{0.5em}}nums$ 分别进行递归分解。
3. 最终将数组分解为 $n$ 个长度均为 $1$ 的有序子数组。
2. **归并过程**:从长度为 $1$ 的有序子数组开始,依次将有序数组两两合并,直到合并成一个长度为 $n$ 的有序数组。
1. 使用数组变量 $nums$ 存放合并后的有序数组。
2. 使用两个指针 $left\underline{}i$、$right\underline{}i$ 分别指向两个有序子数组 $left\underline{}nums$、$right\underline{}nums$ 的开始位置。
2. 使用两个指针 $left\underline{\hspace{0.5em}}i$、$right\underline{\hspace{0.5em}}i$ 分别指向两个有序子数组 $left\underline{\hspace{0.5em}}nums$、$right\underline{\hspace{0.5em}}nums$ 的开始位置。
3. 比较两个指针指向的元素,将两个有序子数组中较小元素依次存入到结果数组 $nums$ 中,并将指针移动到下一位置。
4. 重复步骤 $3$,直到某一指针到达子数组末尾。
5. 将另一个子数组中的剩余元素存入到结果数组 $nums$ 中。
Expand Down
12 changes: 6 additions & 6 deletions docs/ch01/01.03/01.03.11-Array-Counting-Sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
## 2. 计数排序算法步骤

1. **计算排序范围**:遍历数组,找出待排序序列中最大值元素 $nums\underline{}max$ 和最小值元素 $nums\underline{}min$,计算出排序范围为 $nums\underline{}max - nums\underline{}min + 1$。
1. **计算排序范围**:遍历数组,找出待排序序列中最大值元素 $nums\underline{\hspace{0.5em}}max$ 和最小值元素 $nums\underline{\hspace{0.5em}}min$,计算出排序范围为 $nums\underline{\hspace{0.5em}}max - nums\underline{\hspace{0.5em}}min + 1$。
2. **定义计数数组**:定义一个大小为排序范围的计数数组 $counts$,用于统计每个元素的出现次数。其中:
1. 数组的索引值 $num - nums\underline{}min$ 表示元素的值为 $num$。
2. 数组的值 $counts[num - nums\underline{}min]$ 表示元素 $num$ 的出现次数。
1. 数组的索引值 $num - nums\underline{\hspace{0.5em}}min$ 表示元素的值为 $num$。
2. 数组的值 $counts[num - nums\underline{\hspace{0.5em}}min]$ 表示元素 $num$ 的出现次数。

3. **对数组元素进行计数统计**:遍历待排序数组 $nums$,对每个元素在计数数组中进行计数,即将待排序数组中「每个元素值减去最小值」作为索引,将「对计数数组中的值」加 $1$,即令 $counts[num - nums\underline{}min]$ 加 $1$。
4. **生成累积计数数组**:从 $counts$ 中的第 $1$ 个元素开始,每一项累家前一项和。此时 $counts[num - nums\underline{}min]$ 表示值为 $num$ 的元素在排序数组中最后一次出现的位置。
3. **对数组元素进行计数统计**:遍历待排序数组 $nums$,对每个元素在计数数组中进行计数,即将待排序数组中「每个元素值减去最小值」作为索引,将「对计数数组中的值」加 $1$,即令 $counts[num - nums\underline{\hspace{0.5em}}min]$ 加 $1$。
4. **生成累积计数数组**:从 $counts$ 中的第 $1$ 个元素开始,每一项累家前一项和。此时 $counts[num - nums\underline{\hspace{0.5em}}min]$ 表示值为 $num$ 的元素在排序数组中最后一次出现的位置。
5. **逆序填充目标数组**:逆序遍历数组 $nums$,将每个元素 $num$ 填入正确位置。
1. 将其填充到结果数组 $res$ 的索引 $counts[num - nums\underline{}min]$ 处。
1. 将其填充到结果数组 $res$ 的索引 $counts[num - nums\underline{\hspace{0.5em}}min]$ 处。
2. 放入后,令累积计数数组中对应索引减 $1$,从而得到下个元素 $num$ 的放置位置。

我们以 $[3, 0, 4, 2, 5, 1, 3, 1, 4, 5]$ 为例,演示一下计数排序的整个步骤。
Expand Down
18 changes: 9 additions & 9 deletions docs/ch01/01.05/01.05.01-Array-Two-Pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class Solution:

#### 2.6.2 题目大意

**描述**:给定 $n$ 个非负整数 $a_1,a_2, ...,a_n$,每个数代表坐标中的一个点 $(i, a_i)$。在坐标内画 $n$ 条垂直线,垂直线 $i$ 的两个端点分别为 $(i, a_i)$ 和 $(i, 0)$。
**描述**:给定 $n$ 个非负整数 $a_1, a_2, ..., a_n$,每个数代表坐标中的一个点 $(i, a_i)$。在坐标内画 $n$ 条垂直线,垂直线 $i$ 的两个端点分别为 $(i, a_i)$ 和 $(i, 0)$。

**要求**:找出其中的两条线,使得它们与 $x$ 轴共同构成的容器可以容纳最多的水。

Expand Down Expand Up @@ -382,10 +382,10 @@ class Solution:

### 4.1 分离双指针求解步骤

1. 使用两个指针 $left\underline{}1$、$left\underline{}2$。$left\underline{}1$ 指向第一个数组的第一个元素,即:$left\underline{}1 = 0$,$left\underline{}2$ 指向第二个数组的第一个元素,即:$left\underline{}2 = 0$。
2. 当满足一定条件时,两个指针同时右移,即 $left\underline{}1 += 1$、$left\underline{}2 += 1$。
3. 当满足另外一定条件时,将 $left\underline{}1$ 指针右移,即 $left\underline{}1 += 1$。
4. 当满足其他一定条件时,将 $left\underline{}2$ 指针右移,即 $left\underline{}2 += 1$。
1. 使用两个指针 $left\underline{\hspace{0.5em}}1$、$left\underline{\hspace{0.5em}}2$。$left\underline{\hspace{0.5em}}1$ 指向第一个数组的第一个元素,即:$left\underline{\hspace{0.5em}}1 = 0$,$left\underline{\hspace{0.5em}}2$ 指向第二个数组的第一个元素,即:$left\underline{\hspace{0.5em}}2 = 0$。
2. 当满足一定条件时,两个指针同时右移,即 $left\underline{\hspace{0.5em}}1 += 1$、$left\underline{\hspace{0.5em}}2 += 1$。
3. 当满足另外一定条件时,将 $left\underline{\hspace{0.5em}}1$ 指针右移,即 $left\underline{\hspace{0.5em}}1 += 1$。
4. 当满足其他一定条件时,将 $left\underline{\hspace{0.5em}}2$ 指针右移,即 $left\underline{\hspace{0.5em}}2 += 1$。
5. 当其中一个数组遍历完时或者满足其他特殊条件时跳出循环体。

### 4.2 分离双指针伪代码模板
Expand Down Expand Up @@ -450,10 +450,10 @@ while left_1 < len(nums1) and left_2 < len(nums2):
##### 思路 1:分离双指针

1. 对数组 $nums1$、$nums2$ 先排序。
2. 使用两个指针 $left\underline{}1$、$left\underline{}2$。$left\underline{}1$ 指向第一个数组的第一个元素,即:$left\underline{}1 = 0$,$left\underline{}2$ 指向第二个数组的第一个元素,即:$left\underline{}2 = 0$。
3. 如果 $nums1[left\underline{}1] == nums2[left\underline{}2]$,则将其加入答案数组(注意去重),并将 $left\underline{}1$ 和 $left\underline{}2$ 右移。
4. 如果 $nums1[left\underline{}1] < nums2[left\underline{}2]$,则将 $left\underline{}1$ 右移。
5. 如果 $nums1[left\underline{}1] > nums2[left\underline{}2]$,则将 $left\underline{}2$ 右移。
2. 使用两个指针 $left\underline{\hspace{0.5em}}1$、$left\underline{\hspace{0.5em}}2$。$left\underline{\hspace{0.5em}}1$ 指向第一个数组的第一个元素,即:$left\underline{\hspace{0.5em}}1 = 0$,$left\underline{\hspace{0.5em}}2$ 指向第二个数组的第一个元素,即:$left\underline{\hspace{0.5em}}2 = 0$。
3. 如果 $nums1[left\underline{\hspace{0.5em}}1] == nums2[left\underline{\hspace{0.5em}}2]$,则将其加入答案数组(注意去重),并将 $left\underline{\hspace{0.5em}}1$ 和 $left\underline{\hspace{0.5em}}2$ 右移。
4. 如果 $nums1[left\underline{\hspace{0.5em}}1] < nums2[left\underline{\hspace{0.5em}}2]$,则将 $left\underline{\hspace{0.5em}}1$ 右移。
5. 如果 $nums1[left\underline{\hspace{0.5em}}1] > nums2[left\underline{\hspace{0.5em}}2]$,则将 $left\underline{\hspace{0.5em}}2$ 右移。
6. 最后返回答案数组。

##### 思路 1:代码
Expand Down
20 changes: 10 additions & 10 deletions docs/ch01/01.05/01.05.05-Array-Sliding-Window.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@

### 3.1 固定长度滑动窗口算法步骤

假设窗口的固定大小为 $window\underline{}size$。
假设窗口的固定大小为 $window\underline{\hspace{0.5em}}size$。

1. 使用两个指针 $left$、$right$。初始时,$left$、$right$ 都指向序列的第一个元素,即:$left = 0$,$right = 0$,区间 $[left, right]$ 被称为一个「窗口」。
2. 当窗口未达到 $window\underline{}size$ 大小时,不断移动 $right$,先将数组前 $window\underline{}size$ 个元素填入窗口中,即 `window.append(nums[right])`
2. 当窗口达到 $window\underline{}size$ 大小时,即满足 `right - left + 1 >= window_size` 时,判断窗口内的连续元素是否满足题目限定的条件。
2. 当窗口未达到 $window\underline{\hspace{0.5em}}size$ 大小时,不断移动 $right$,先将数组前 $window\underline{\hspace{0.5em}}size$ 个元素填入窗口中,即 `window.append(nums[right])`
2. 当窗口达到 $window\underline{\hspace{0.5em}}size$ 大小时,即满足 `right - left + 1 >= window_size` 时,判断窗口内的连续元素是否满足题目限定的条件。
1. 如果满足,再根据要求更新最优解。
2. 然后向右移动 $left$,从而缩小窗口长度,即 `left += 1`,使得窗口大小始终保持为 $window\underline{}size$。
2. 然后向右移动 $left$,从而缩小窗口长度,即 `left += 1`,使得窗口大小始终保持为 $window\underline{\hspace{0.5em}}size$。
3. 向右移动 $right$,将元素填入窗口中,即 `window.append(nums[right])`
4. 重复 $2 \sim 4$ 步,直到 $right$ 到达数组末尾。

Expand Down Expand Up @@ -109,7 +109,7 @@ while right < len(nums):

这道题目是典型的固定窗口大小的滑动窗口题目。窗口大小为 $k$。具体做法如下:

1. $ans$ 用来维护答案数目。$window\underline{}sum$ 用来维护窗口中元素的和。
1. $ans$ 用来维护答案数目。$window\underline{\hspace{0.5em}}sum$ 用来维护窗口中元素的和。
2. $left$ 、$right$ 都指向序列的第一个元素,即:$left = 0$,$right = 0$。
3. 向右移动 $right$,先将 $k$ 个元素填入窗口中,即 `window_sum += arr[right]`
4. 当窗口元素个数为 $k$ 时,即满足 `right - left + 1 >= k` 时,判断窗口内的元素和平均值是否大于等于阈值 $threshold$。
Expand Down Expand Up @@ -303,8 +303,8 @@ class Solution:
用滑动窗口来记录连续子数组的和,设定两个指针:$left$、$right$,分别指向滑动窗口的左右边界,保证窗口中的和刚好大于等于 $target$。

1. 一开始,$left$、$right$ 都指向 $0$。
2. 向右移动 $right$,将最右侧元素加入当前窗口和 $window\underline{}sum$ 中。
3. 如果 $window\underline{}sum \ge target$,则不断右移 $left$,缩小滑动窗口长度,并更新窗口和的最小值,直到 $window\underline{}sum < target$。
2. 向右移动 $right$,将最右侧元素加入当前窗口和 $window\underline{\hspace{0.5em}}sum$ 中。
3. 如果 $window\underline{\hspace{0.5em}}sum \ge target$,则不断右移 $left$,缩小滑动窗口长度,并更新窗口和的最小值,直到 $window\underline{\hspace{0.5em}}sum < target$。
4. 然后继续右移 $right$,直到 $right \ge len(nums)$ 结束。
5. 输出窗口和的最小值作为答案。

Expand Down Expand Up @@ -376,10 +376,10 @@ class Solution:

##### 思路 1:滑动窗口(不定长度)

1. 设定两个指针:$left$、$right$,分别指向滑动窗口的左右边界,保证窗口内所有数的乘积 $window\underline{}product$ 都小于 $k$。使用 $window\underline{}product$ 记录窗口中的乘积值,使用 $count$ 记录符合要求的子数组个数。
1. 设定两个指针:$left$、$right$,分别指向滑动窗口的左右边界,保证窗口内所有数的乘积 $window\underline{\hspace{0.5em}}product$ 都小于 $k$。使用 $window\underline{\hspace{0.5em}}product$ 记录窗口中的乘积值,使用 $count$ 记录符合要求的子数组个数。
2. 一开始,$left$、$right$ 都指向 $0$。
3. 向右移动 $right$,将最右侧元素加入当前子数组乘积 $window\underline{}product$ 中。
4. 如果 $window\underline{}product \ge k$,则不断右移 $left$,缩小滑动窗口长度,并更新当前乘积值 $window\underline{}product$ 直到 $window\underline{}product < k$。
3. 向右移动 $right$,将最右侧元素加入当前子数组乘积 $window\underline{\hspace{0.5em}}product$ 中。
4. 如果 $window\underline{\hspace{0.5em}}product \ge k$,则不断右移 $left$,缩小滑动窗口长度,并更新当前乘积值 $window\underline{\hspace{0.5em}}product$ 直到 $window\underline{\hspace{0.5em}}product < k$。
5. 记录累积答案个数加 $1$,继续右移 $right$,直到 $right \ge len(nums)$ 结束。
6. 输出累积答案个数。

Expand Down
Loading

0 comments on commit a9fb47a

Please sign in to comment.