Skip to content

Commit 8630e96

Browse files
committed
修复和优化语句表述
1 parent d5e9252 commit 8630e96

File tree

1 file changed

+67
-143
lines changed

1 file changed

+67
-143
lines changed

docs/07_algorithm/07_01_enumeration_algorithm.md

Lines changed: 67 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,79 @@
11
## 1. 枚举算法简介
22

3-
> **枚举算法(Enumeration Algorithm)**:也称为穷举算法,指的是按照问题本身的性质,一一列举出该问题所有可能的解,并在逐一列举的过程中,将它们逐一与目标状态进行比较以得出满足问题要求的解。在列举的过程中,既不能遗漏也不能重复
3+
> **枚举算法(Enumeration Algorithm)**,又称穷举算法,是指根据问题的特点,逐一列出所有可能的解,并与目标条件进行比较,找出满足要求的答案。枚举时要确保不遗漏、不重复
44
5-
枚举算法的核心思想是:通过列举问题的所有状态,将它们逐一与目标状态进行比较,从而得到满足条件的解
5+
枚举算法的核心思想就是:遍历所有可能的状态,逐个判断是否满足条件,找到符合要求的解
66

7-
由于枚举算法要通过列举问题的所有状态来得到满足条件的解,因此,在问题规模变大时,其效率一般是比较低的。但是枚举算法也有自己特有的优点
7+
由于需要遍历所有状态,枚举算法在问题规模较大时效率较低。但它也有明显优点
88

9-
1. 多数情况下容易编程实现,也容易调试
10-
2. 建立在考察大量状态、甚至是穷举所有状态的基础上,所以算法的正确性比较容易证明
9+
1. 实现简单,易于编程和调试
10+
2. 基于穷举所有情况,正确性容易验证
1111

12-
所以,枚举算法通常用于求解问题规模比较小的问题,或者作为求解问题的一个子算法出现,通过枚举一些信息并进行保存,而这些消息的有无对主算法效率的高低有着较大影响
12+
因此,枚举算法常用于小规模问题,或作为其他算法的辅助工具,通过枚举部分信息来提升主算法的效率
1313

1414
## 2. 枚举算法的解题思路
1515

1616
### 2.1 枚举算法的解题思路
1717

18-
枚举算法是设计最简单、最基本的搜索算法。是我们在遇到问题时,最应该优先考虑的算法
18+
枚举算法是最简单、最基础的搜索方法,通常是遇到问题时的首选方案
1919

20-
因为其实现足够简单,所以在遇到问题时,我们往往可以先通过枚举算法尝试解决问题,然后在此基础上,再去考虑其他优化方法和解题思路
20+
由于实现简单,我们可以先用枚举算法尝试解决问题,再考虑是否需要优化
2121

22-
采用枚举算法解题的一般思路如下
22+
枚举算法的基本步骤如下
2323

24-
1. 确定枚举对象、枚举范围和判断条件,并判断条件设立的正确性
25-
2. 一一枚举可能的情况,并验证是否是问题的解
26-
3. 考虑提高枚举算法的效率
24+
1. 明确需要枚举的对象、枚举范围和约束条件
25+
2. 逐一枚举所有可能情况,判断是否满足题意
26+
3. 思考如何提升枚举效率
2727

28-
我们可以从下面几个方面考虑提高算法的效率
28+
提升效率的常用方法有
2929

30-
1. 抓住问题状态的本质,尽可能缩小问题状态空间的大小
31-
2. 加强约束条件,缩小枚举范围
32-
3. 根据某些问题特有的性质,例如对称性等,避免对本质相同的状态重复求解
30+
- 抓住问题本质,尽量缩小状态空间
31+
- 增加约束条件,减少无效枚举
32+
- 利用某些问题特有的性质(例如对称性等),避免重复计算
3333

3434
### 2.2 枚举算法的简单应用
3535

36-
下面举个著名的例子:「百钱买百鸡问题」。这个问题是我国古代数学家张丘在「算经」一书中提出的。该问题叙述如下
36+
以经典的「百钱买百鸡问题」为例
3737

38-
> **百钱买百鸡问题**鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则鸡翁、鸡母、鸡雏各几何
38+
> **问题**公鸡 5 元/只,母鸡 3 元/只,小鸡 1 元/3 只。用 100 元买 100 只鸡,问各买多少只
3939
40-
翻译一下,意思就是:公鸡一只五块钱,母鸡一只三块钱,小鸡三只一块钱。现在我们用 $100$ 块钱买了 $100$ 只鸡,问公鸡、母鸡、小鸡各买了多少只?
40+
**解题步骤**
4141

42-
下面我们根据算法的一般思路来解决一下这道题。
42+
1. **确定枚举对象和范围**
43+
- 枚举对象:公鸡数 $x$,母鸡数 $y$,小鸡数 $z$
44+
- 枚举范围:$0 \le x, y, z \le 100$
45+
- 约束条件:$5x + 3y + \frac{z}{3} = 100$ 且 $x + y + z = 100$
4346

44-
1. 确定枚举对象、枚举范围和判断条件,并判断条件设立的正确性。
47+
2. **暴力枚举**
4548

46-
1. 确定枚举对象:枚举对象为公鸡、母鸡、小鸡的只数,那么我们可以用变量 $x$、$y$、$z$ 分别来代表公鸡、母鸡、小鸡的只数。
47-
2. 确定枚举范围:因为总共买了 $100$ 只鸡,所以 $0 \le x, y, z \le 100$,则 $x$、$y$、$z$ 的枚举范围为 $[0, 100]$。
48-
3. 确定判断条件:根据题意,我们可以列出两个方程式:$5 \times x + 3 \times y + \frac{z}{3} = 100$,$x + y + z = 100$。在枚举 $x$、$y$、$z$ 的过程中,我们可以根据这两个方程式来判断是否当前状态是否满足题意。
49+
```python
50+
class Solution:
51+
def buyChicken(self):
52+
for x in range(101):
53+
for y in range(101):
54+
for z in range(101):
55+
if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100 and x + y + z == 100:
56+
print("公鸡 %s 只,母鸡 %s 只,小鸡 %s" % (x, y, z))
57+
```
4958

50-
2. 一一枚举可能的情况,并验证是否是问题的解。
59+
3. **优化枚举效率**
60+
- 利用 $z = 100 - x - y$ 减少一重循环
61+
- 缩小枚举范围:$x \in [0, 20]$,$y \in [0, 33]$
5162

52-
1. 根据枚举对象、枚举范围和判断条件,我们可以顺利写出对应的代码。
53-
54-
```python
55-
class Solution:
56-
def buyChicken(self):
57-
for x in range(101):
58-
for y in range(101):
59-
for z in range(101):
60-
if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100 and x + y + z == 100:
61-
print("公鸡 %s 只,母鸡 %s 只,小鸡 %s" % (x, y, z))
62-
```
63-
64-
3. 考虑提高枚举算法的效率。
65-
66-
1. 在上面的代码中,我们枚举了 $x$$y$$z$,但其实根据方程式 $x + y + z = 100$,得知:$z$ 可以通过 $z = 100 - x - y$ 而得到,这样我们就不用再枚举 $z$ 了。
67-
2. 在上面的代码中,对 $x$$y$ 的枚举范围是 $[0, 100]$,但其实如果所有钱用来买公鸡,最多只能买 $20$ 只,同理,全用来买母鸡,最多只能买 $33$ 只。所以对 $x$ 的枚举范围可改为 $[0, 20]$$y$ 的枚举范围可改为 $[0, 33]$
68-
69-
```python
70-
class Solution:
71-
def buyChicken(self):
72-
for x in range(21):
73-
for y in range(34):
74-
z = 100 - x - y
75-
if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100:
76-
print("公鸡 %s 只,母鸡 %s 只,小鸡 %s" % (x, y, z))
77-
```
63+
```python
64+
class Solution:
65+
def buyChicken(self):
66+
for x in range(21):
67+
for y in range(34):
68+
z = 100 - x - y
69+
if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100:
70+
print("公鸡 %s 只,母鸡 %s 只,小鸡 %s" % (x, y, z))
71+
```
7872

7973

8074
## 3. 枚举算法的应用
8175

82-
### 3.1 两数之和
76+
### 3.1 经典例题:两数之和
8377

8478
#### 3.1.1 题目链接
8579

@@ -117,109 +111,38 @@
117111

118112
#### 3.1.3 解题思路
119113

120-
这里说下枚举算法的解题思路。
121-
122114
##### 思路 1:枚举算法
123115

124-
1. 使用两重循环枚举数组中每一个数 $nums[i]$、$nums[j]$,判断所有的 $nums[i] + nums[j]$ 是否等于 $target$。
125-
2. 如果出现 $nums[i] + nums[j] == target$,则说明数组中存在和为 $target$ 的两个整数,将两个整数的下标 $i$、$j$ 输出即可
116+
1. 通过两重循环,依次枚举数组中所有可能的下标对 $(i, j)$(其中 $i < j$),判断 $nums[i] + nums[j]$ 是否等于 $target$。
117+
2. 一旦找到满足条件的下标对,即 $nums[i] + nums[j] == target$,立即返回这两个下标 $[i, j]$ 作为答案
126118

127119
##### 思路 1:代码
128120

129121
```python
130122
class Solution:
131123
def twoSum(self, nums: List[int], target: int) -> List[int]:
124+
# 遍历第一个数的下标
132125
for i in range(len(nums)):
126+
# 遍历第二个数的下标(只需从i+1开始,避免和自身重复)
133127
for j in range(i + 1, len(nums)):
134-
if i != j and nums[i] + nums[j] == target:
135-
return [i, j]
136-
return []
128+
# 判断两数之和是否等于目标值
129+
if nums[i] + nums[j] == target:
130+
return [i, j] # 返回下标对
131+
return [] # 如果没有找到,返回空列表
137132
```
138133

139134
##### 思路 1:复杂度分析
140135

141136
- **时间复杂度**:$O(n^2)$,其中 $n$ 为数组 $nums$ 的元素数量。
142137
- **空间复杂度**:$O(1)$。
143138

144-
### 3.2 计数质数
139+
### 3.2 统计平方和三元组的数目
145140

146141
#### 3.2.1 题目链接
147142

148-
- [204. 计数质数 - 力扣(LeetCode)](https://leetcode.cn/problems/count-primes/)
149-
150-
#### 3.2.2 题目大意
151-
152-
**描述**:给定 一个非负整数 $n$。
153-
154-
**要求**:统计小于 $n$ 的质数数量。
155-
156-
**说明**
157-
158-
- $0 \le n \le 5 \times 10^6$。
159-
160-
**示例**
161-
162-
- 示例 1:
163-
164-
```python
165-
输入 n = 10
166-
输出 4
167-
解释 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7
168-
```
169-
170-
- 示例 2:
171-
172-
```python
173-
输入:n = 1
174-
输出:0
175-
```
176-
177-
#### 3.2.3 解题思路
178-
179-
这里说下枚举算法的解题思路(注意:提交会超时,只是讲解一下枚举算法的思路)。
180-
181-
##### 思路 1:枚举算法(超时)
182-
183-
对于小于 $n$ 的每一个数 $x$,我们可以枚举区间 $[2, x - 1]$ 上的数是否是 $x$ 的因数,即是否存在能被 $x$ 整除的数。如果存在,则该数 $x$ 不是质数。如果不存在,则该数 $x$ 是质数。
184-
185-
这样我们就可以通过枚举 $[2, n - 1]$ 上的所有数 $x$,并判断 $x$ 是否为质数。
186-
187-
在遍历枚举的同时,我们维护一个用于统计小于 $n$ 的质数数量的变量 $cnt$。如果符合要求,则将计数 $cnt$ 加 $1$。最终返回该数目作为答案。
188-
189-
考虑到如果 $i$ 是 $x$ 的因数,则 $\frac{x}{i}$ 也必然是 $x$ 的因数,则我们只需要检验这两个因数中的较小数即可。而较小数一定会落在 $[2, \sqrt x]$ 上。因此我们在检验 $x$ 是否为质数时,只需要枚举 $[2, \sqrt x]$ 中的所有数即可。
190-
191-
利用枚举算法单次检查单个数的时间复杂度为 $O(\sqrt{n})$,检查 $n$ 个数的整体时间复杂度为 $O(n \sqrt{n})$。
192-
193-
##### 思路 1:代码
194-
195-
```python
196-
class Solution:
197-
def isPrime(self, x):
198-
for i in range(2, int(pow(x, 0.5)) + 1):
199-
if x % i == 0:
200-
return False
201-
return True
202-
203-
def countPrimes(self, n: int) -> int:
204-
cnt = 0
205-
for x in range(2, n):
206-
if self.isPrime(x):
207-
cnt += 1
208-
return cnt
209-
```
210-
211-
##### 思路 1:复杂度分析
212-
213-
- **时间复杂度**:$O(n \times \sqrt{n})$。
214-
- **空间复杂度**:$O(1)$。
215-
216-
### 3.3 统计平方和三元组的数目
217-
218-
#### 3.3.1 题目链接
219-
220143
- [1925. 统计平方和三元组的数目 - 力扣(LeetCode)](https://leetcode.cn/problems/count-square-sum-triples/)
221144

222-
#### 3.3.2 题目大意
145+
#### 3.2.2 题目大意
223146

224147
**描述**:给你一个整数 $n$。
225148

@@ -248,30 +171,31 @@ class Solution:
248171
解释:平方和三元组为 (3,4,5),(4,3,5),(6,8,10) 和 (8,6,10)。
249172
```
250173

251-
#### 3.3.3 解题思路
174+
#### 3.2.3 解题思路
252175

253176
##### 思路 1:枚举算法
254177

255-
我们可以在 $[1, n]$ 区间中枚举整数三元组 $(a, b, c)$ 中的 $a$ 和 $b$。然后判断 $a^2 + b^2$ 是否小于等于 $n$,并且是完全平方数
178+
直接枚举 $a$ 和 $b$,计算 $c^2 = a^2 + b^2$,判断 $c$ 是否为整数且 $1 \leq c \leq n$,如果满足条件则计数加一,最后返回总数
256179

257-
在遍历枚举的同时,我们维护一个用于统计平方和三元组数目的变量 $cnt$。如果符合要求,则将计数 $cnt$ 加 $1$。最终,我们返回该数目作为答案
180+
该方法时间复杂度为 $O(n^2)$
258181

259-
利用枚举算法统计平方和三元组数目的时间复杂度为 $O(n^2)$。
260182

261-
- 注意:在计算中,为了防止浮点数造成的误差,并且两个相邻的完全平方正数之间的距离一定大于 $1$,所以我们可以用 $\sqrt{a^2 + b^2 + 1}$ 来代替 $\sqrt{a^2 + b^2}$。
183+
- 注意:为避免浮点误差,可以用 $\sqrt{a^2 + b^2 + 1}$ 代替 $\sqrt{a^2 + b^2}$,这样判断 $c$ 是否为整数更安全
262184

263185
##### 思路 1:代码
264186

265187
```python
266188
class Solution:
267189
def countTriples(self, n: int) -> int:
268-
cnt = 0
269-
for a in range(1, n + 1):
270-
for b in range(1, n + 1):
190+
cnt = 0 # 统计满足条件的三元组个数
191+
for a in range(1, n + 1): # 枚举 a
192+
for b in range(1, n + 1): # 枚举 b
193+
# 计算 c,注意加 1 防止浮点误差
271194
c = int(sqrt(a * a + b * b + 1))
195+
# 判断 c 是否在范围内,且 a^2 + b^2 == c^2
272196
if c <= n and a * a + b * b == c * c:
273-
cnt += 1
274-
return cnt
197+
cnt += 1 # 满足条件,计数加一
198+
return cnt # 返回最终统计结果
275199
```
276200

277201
##### 思路 1:复杂度分析

0 commit comments

Comments
 (0)