Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4b6f2f0

Browse files
committed
修复和优化语句表述
1 parent d517b53 commit 4b6f2f0

7 files changed

+959
-988
lines changed

‎docs/06_graph/06_02_graph_structure.md‎

Lines changed: 386 additions & 383 deletions
Large diffs are not rendered by default.

‎docs/06_graph/06_03_graph_dfs.md‎

Lines changed: 112 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
## 1. 深度优先搜索简介
22

3-
> **深度优先搜索算法(Depth First Search)**:英文缩写为 DFS,是一种用于搜索树或图结构的算法。深度优先搜索算法采用了回溯思想,从起始节点开始,沿着一条路径尽可能深入地访问节点,直到无法继续前进时为止,然后回溯到上一个未访问的节点,继续深入搜索,直到完成整个搜索过程
3+
> **深度优先搜索算法(Depth First Search,简称 DFS)**:是一种用于遍历或搜索树、图等结构的经典算法。其核心思想是「沿一条路径尽可能深入」,遇到无法继续的节点时再回溯到上一个分叉点,继续探索其他路径,直到遍历完整个结构或找到目标为止
44
5-
深度优先搜索算法中所谓的深度优先,就是说优先沿着一条路径走到底,直到无法继续深入时再回头
5+
深度优先的含义,就是每次优先选择一条路径一直走到底,只有在无法继续时才回退,尝试其他分支
66

7-
在深度优先遍历的过程中,我们需要将当前遍历节点 $u$ 的相邻节点暂时存储起来,以便于在回退的时候可以继续访问它们。遍历到的节点顺序符合「后进先出」的特点,这正是「递归」和「堆栈」所遵循的规律,所以深度优先搜索可以通过「递归」或者「堆栈」来实现
7+
在 DFS 的遍历过程中,我们通常需要暂存当前节点 $u$ 的相邻节点,以便回溯时继续访问未遍历的节点。由于遍历顺序具有「后进先出」的特性,DFS 可以通过递归(系统栈)或显式使用栈结构来实现
88

99
## 2. 深度优先搜索算法步骤
1010

11-
接下来我们以一个无向图为例,介绍一下深度优先搜索的算法步骤。
11+
下面以无向图为例,简要梳理深度优先搜索的基本流程:
1212

13-
1. 选择起始节点 $u,ドル并将其标记为已访问
14-
2. 检查当前节点是否为目标节点(看具体题目要求)。
15-
3. 如果当前节点 $u$ 是目标节点,则直接返回结果
16-
4. 如果当前节点 $u$ 不是目标节点,则遍历当前节点 $u$ 的所有未访问邻接节点
17-
5. 对每个未访问的邻接节点 $v,ドル从节点 $v$ 出发继续进行深度优先搜索(递归)
18-
6. 如果节点 $u$ 没有未访问的相邻节点,回溯到上一个节点,继续搜索其他路径
19-
7. 重复 2ドル \sim 6$ 步骤,直到遍历完整个图或找到目标节点为止。
13+
1. 选定起始节点 $u,ドル将其标记为已访问
14+
2. 判断当前节点 $u$ 是否为目标节点(具体依据题目要求)。
15+
3. 如果 $u$ 为目标节点,直接返回结果
16+
4. 如果 $u$ 不是目标节点,则遍历 $u$ 的所有未被访问的邻接节点
17+
5. 对每一个未访问的邻接节点 $v,ドル递归地从 $v$ 继续进行深度优先搜索
18+
6. 如果 $u$ 没有未访问的邻接节点,则回溯到上一个节点,继续探索其他分支
19+
7. 重复步骤 2ドル \sim 6,ドル直到遍历完整个图或找到目标节点为止。
2020

2121
::: tabs#DFS
2222

@@ -46,105 +46,126 @@
4646

4747
:::
4848

49-
## 3. 基于递归实现的深度优先搜索
49+
## 3. 基于递归的深度优先搜索
5050

51-
### 3.1 基于递归实现的深度优先搜索算法步骤
51+
### 3.1 基于递归的深度优先搜索算法步骤
5252

53-
深度优先搜索算法可以通过递归来实现,以下是基于递归实现的深度优先搜索算法步骤:
53+
深度优先搜索(DFS)可以通过递归方式实现。其基本递归流程如下:
5454

55-
1. 定义 $graph$ 为存储无向图的嵌套数组变量,$visited$ 为标记访问节点的集合变量。$u$ 为当前遍历边的开始节点。定义 `def dfs_recursive(graph, u, visited):` 为递归实现的深度优先搜索方法
56-
2. 选择起始节点 $u$,并将其标记为已访问,即将节点 $u$ 放入 $visited$ 中(`visited.add(u)`)。
57-
3. 检查当前节点 $u$ 是否为目标节点(看具体题目要求)。
58-
4. 如果当前节点 $u$ 是目标节点,则直接返回结果
59-
5. 如果当前节点 $u$ 不是目标节点,则遍历当前节点 $u$ 的所有未访问邻接节点
60-
6. 对每个未访问的邻接节点 $v,ドル从节点 $v$ 出发继续进行深度优先搜索(递归),即调用 `dfs_recursive(graph, v, visited)`
61-
7. 如果节点 $u$ 没有未访问的相邻节点,则回溯到最近访问的节点,继续搜索其他路径
62-
8. 重复 3ドル \sim 7$ 步骤,直到遍历完整个图或找到目标节点为止。
55+
1. $graph$ 为无向图的邻接表,$visited$ 为已访问节点的集合,$u$ 为当前节点。定义递归函数 `def dfs_recursive(graph, u, visited):`
56+
2. 将起始节点 $u$ 标记为已访问(`visited.add(u)`)。
57+
3. 判断当前节点 $u$ 是否为目标节点(具体依据题目要求)。
58+
4. 如果 $u$ 为目标节点,直接返回结果
59+
5. 如果 $u$ 不是目标节点,则遍历 $u$ 的所有未访问的邻接节点 $v$
60+
6. 对每个未访问的邻接节点 $v,ドル递归调用 `dfs_recursive(graph, v, visited)` 继续搜索
61+
7. 如果 $u$ 没有未访问的邻接节点,则自动回溯到上一个节点,继续探索其他分支
62+
8. 重复步骤 3ドル \sim 7,ドル直到遍历完整个图或找到目标节点为止。
6363

64-
### 3.2 基于递归实现的深度优先搜索实现代码
64+
### 3.2 基于递归的深度优先搜索实现代码
6565

6666
```python
6767
class Solution:
6868
def dfs_recursive(self, graph, u, visited):
69-
print(u) # 访问节点
70-
visited.add(u) # 节点 u 标记其已访问
71-
69+
"""
70+
递归实现深度优先搜索(DFS)
71+
:param graph: 字典表示的邻接表,key为节点,value为邻接节点列表
72+
:param u: 当前访问的节点
73+
:param visited: 已访问节点的集合
74+
"""
75+
print(u) # 访问当前节点 u
76+
visited.add(u) # 标记节点 u 已访问
77+
78+
# 遍历所有邻接节点
7279
for v in graph[u]:
73-
if v not in visited: # 节点 v 未访问过
74-
# 深度优先搜索遍历节点
80+
if v not in visited: # 如果邻接节点 v 未被访问
81+
# 递归访问邻接节点 v
7582
self.dfs_recursive(graph, v, visited)
7683

7784

85+
# 示例图(邻接表形式,节点为字符串,边为无向边)
7886
graph = {
79-
"A": ["B", "C"],
80-
"B": ["A", "C", "D"],
81-
"C": ["A", "B", "D", "E"],
82-
"D": ["B", "C", "E", "F"],
83-
"E": ["C", "D"],
84-
"F": ["D", "G"],
85-
"G": []
87+
"1": ["2", "3"],
88+
"2": ["1", "3", "4"],
89+
"3": ["1", "2", "4", "5"],
90+
"4": ["2", "3", "5", "6"],
91+
"5": ["3", "4"],
92+
"6": ["4", "7"],
93+
"7": []
8694
}
8795

88-
# 基于递归实现的深度优先搜索
96+
# 初始化已访问节点集合
8997
visited = set()
90-
Solution().dfs_recursive(graph, "A", visited)
98+
# 从节点 "1" 开始进行深度优先搜索
99+
Solution().dfs_recursive(graph, "1", visited)
91100
```
92101

93-
## 4. 基于堆栈实现的深度优先搜索
102+
## 4. 基于堆栈的深度优先搜索
94103

95-
### 4.1 基于堆栈实现的深度优先搜索算法步骤
104+
### 4.1 基于堆栈的深度优先搜索算法步骤
96105

97-
深度优先搜索算法除了基于递归实现之外,还可以基于堆栈来实现。同时,为了防止多次遍历同一节点,在使用栈存放节点访问记录时,我们将「当前节点」以及「下一个将要访问的邻接节点下标」一同存入栈中,从而在出栈时,可以通过下标直接找到下一个邻接节点,而不用遍历所有邻接节点
106+
除了递归方式,深度优先搜索(DFS)还可以用显式的栈结构实现。为避免重复访问同一节点,栈中不仅存储当前节点,还记录下一个待访问的邻接节点下标。这样每次出栈时,可以直接定位下一个邻接节点,无需遍历全部邻接点
98107

99-
以下是基于堆栈实现的深度优先搜索的算法步骤:
108+
基于堆栈的 DFS 步骤如下:
100109

101-
1. 定义 $graph$ 为存储无向图的嵌套数组变量,$visited$ 为标记访问节点的集合变量。$start$ 为当前遍历边的开始节点。定义 $stack$ 用于存放节点访问记录的栈结构
110+
1. $graph$ 为无向图的邻接表,$visited$ 为已访问节点集合,$stack$ 为辅助栈
102111
2. 选择起始节点 $u,ドル检查当前节点 $u$ 是否为目标节点(看具体题目要求)。
103-
3. 如果当前节点 $u$ 是目标节点,则直接返回结果。
104-
4. 如果当前节点 $u$ 不是目标节点,则将节点 $u$ 以及节点 $u$ 下一个将要访问的邻接节点下标 0ドル$ 放入栈中,并标记为已访问,即 `stack.append([u, 0])`,`visited.add(u)`
105-
5. 如果栈不为空,取出 $stack$ 栈顶元素节点 $u,ドル以及节点 $u$ 下一个将要访问的邻接节点下标 $i$。
106-
6. 根据节点 $u$ 和下标 $i,ドル取出将要遍历的未访问过的邻接节点 $v$。
107-
7. 将节点 $u$ 以及节点 u 的下一个邻接节点下标 $i + 1$ 放入栈中。
108-
8. 访问节点 $v,ドル并对节点进行相关操作(看具体题目要求)。
109-
9. 将节点 $v$ 以及节点 $v$ 下一个邻接节点下标 0ドル$ 放入栈中,并标记为已访问,即 `stack.append([v, 0])`,`visited.add(v)`
110-
10. 重复步骤 5ドル \sim 9,ドル直到 $stack$ 栈为空或找到目标节点为止。
112+
3. 如果 $u$ 是目标节点,直接返回结果。
113+
4. 如果 $u$ 不是目标节点,将 $[u, 0]$(节点 $u$ 及其下一个邻接节点下标 0ドル$)压入栈,并将 $u$ 标记为已访问:`stack.append([u, 0])`,`visited.add(u)`
114+
5. 当栈非空时,弹出栈顶元素 $[u, i],ドル其中 $i$ 表示下一个待访问的邻接节点下标。
115+
6. 如果 $i$ 小于 $graph[u]$ 的邻接节点数,则取出 $v = graph[u][i]$。
116+
7. 将 $[u, i + 1]$ 压回栈中,表示下次将访问 $u$ 的下一个邻接节点。
117+
8. 如果 $v$ 未被访问,则对 $v$ 进行相关操作(如打印、判定等),并将 $[v, 0]$ 压入栈,同时标记 $v$ 已访问。
118+
9. 重复步骤 5ドル \sim 8,ドル直到栈为空或找到目标节点为止。
119+
120+
这种实现方式可以模拟递归调用过程,且便于控制递归深度,适合递归层数较深或需手动管理回溯的场景。
111121

112122
### 4.2 基于堆栈实现的深度优先搜索实现代码
113123

114124
```python
115125
class Solution:
116126
def dfs_stack(self, graph, u):
117-
print(u) # 访问节点 u
118-
visited, stack = set(), [] # 使用 visited 标记访问过的节点, 使用栈 stack 存放临时节点
119-
120-
stack.append([u, 0]) # 将节点 u,节点 u 的下一个邻接节点下标放入栈中,下次将遍历 graph[u][0]
121-
visited.add(u) # 将起始节点 u 标记为已访问
122-
123-
127+
"""
128+
基于显式栈的深度优先搜索(DFS),适用于无向图/有向图的邻接表表示。
129+
:param graph: dict,邻接表,key为节点,value为邻接节点列表
130+
:param u: 起始节点
131+
"""
132+
visited = set() # 记录已访问节点,防止重复遍历
133+
stack = [] # 显式栈,模拟递归过程
134+
135+
stack.append([u, 0]) # 入栈:节点u及其下一个待访问邻接节点的下标 0
136+
visited.add(u) # 标记起始节点已访问
137+
print(u) # 访问起始节点
138+
124139
while stack:
125-
u, i = stack.pop() # 取出节点 u,以及节点 u 下一个将要访问的邻接节点下标 i
126-
127-
if i < len(graph[u]):
128-
v = graph[u][i] # 取出邻接节点 v
129-
stack.append([u, i + 1]) # 下一次将遍历 graph[u][i + 1]
130-
if v not in visited: # 节点 v 未访问过
131-
print(v) # 访问节点 v
132-
stack.append([v, 0]) # 下一次将遍历 graph[v][0]
133-
visited.add(v) # 将节点 v 标记为已访问
134-
140+
cur, idx = stack.pop() # 取出当前节点及其下一个邻接节点下标
141+
neighbors = graph[cur] # 当前节点的所有邻接节点
142+
143+
# 如果还有未遍历的邻接节点
144+
if idx < len(neighbors):
145+
v = neighbors[idx] # 取出下一个邻接节点
146+
stack.append([cur, idx + 1]) # 当前节点下标 + 1,回溯时继续遍历下一个邻接点
147+
148+
if v not in visited:
149+
print(v) # 访问新节点
150+
visited.add(v) # 标记为已访问
151+
stack.append([v, 0]) # 新节点入栈,准备遍历其邻接节点
152+
153+
# 也可以返回 visited 集合,便于后续处理
154+
# return visited
135155

156+
# 示例图(邻接表形式,节点为字符串,边为无向边)
136157
graph = {
137-
"A": ["B", "C"],
138-
"B": ["A", "C", "D"],
139-
"C": ["A", "B", "D", "E"],
140-
"D": ["B", "C", "E", "F"],
141-
"E": ["C", "D"],
142-
"F": ["D", "G"],
143-
"G": []
158+
"1": ["2", "3"],
159+
"2": ["1", "3", "4"],
160+
"3": ["1", "2", "4", "5"],
161+
"4": ["2", "3", "5", "6"],
162+
"5": ["3", "4"],
163+
"6": ["4", "7"],
164+
"7": []
144165
}
145166

146-
# 基于堆栈实现的深度优先搜索
147-
Solution().dfs_stack(graph, "A")
167+
# 从节点 "1" 开始进行深度优先搜索
168+
Solution().dfs_stack(graph, "1")
148169
```
149170

150171
## 5. 深度优先搜索应用
@@ -198,17 +219,19 @@ Solution().dfs_stack(graph, "A")
198219

199220
#### 5.1.3 解题思路
200221

201-
如果把上下左右相邻的字符 `'1'` 看做是 `1` 个连通块,这道题的目的就是求解一共有多少个连通块
222+
本题实质是统计二维网格中「上下左右」相连的 `'1'` 组成的连通块数量,即岛屿的个数
202223

203-
使用深度优先搜索或者广度优先搜索都可以
224+
可以采用深度优先搜索(DFS)或广度优先搜索(BFS)来实现
204225

205226
##### 思路 1:深度优先搜索
206227

207-
1. 遍历 $grid$。
208-
2. 对于每一个字符为 `'1'` 的元素,遍历其上下左右四个方向,并将该字符置为 `'0'`,保证下次不会被重复遍历。
209-
3. 如果超出边界,则返回 0ドル$。
210-
4. 对于 $(i, j)$ 位置的元素来说,递归遍历的位置就是 $(i - 1, j)$、$(i, j - 1)$、$(i + 1, j)$、$(i, j + 1)$ 四个方向。每次遍历到底,统计数记录一次。
211-
5. 最终统计出深度优先搜索的次数就是我们要求的岛屿数量。
228+
1. 遍历整个 $grid$ 网格。
229+
2. 当遇到字符为 `'1'` 的格子时,说明发现了新的岛屿,执行深度优先搜索(DFS):
230+
- 将当前格子标记为 `'0'`,避免重复访问。
231+
- 递归访问其上下左右四个相邻格子(即 $(i - 1, j)$、$(i + 1, j)$、$(i, j - 1)$、$(i, j + 1)$)。
232+
- 如果递归过程中遇到越界或当前格子为 `'0'`,则直接返回。
233+
3. 每当一次完整的 DFS 结束,岛屿计数加一。
234+
4. 最终 DFS 执行的次数即为岛屿的总数。
212235

213236
##### 思路 1:代码
214237

@@ -290,12 +313,12 @@ class Solution:
290313

291314
##### 思路 1:深度优先搜索
292315

293-
1. 使用哈希表 $visitedDict$ 来存储原图中被访问过的节点和克隆图中对应节点,键值对为「原图被访问过的节点:克隆图中对应节点」
294-
2. 从给定节点开始,以深度优先搜索的方式遍历原图。
295-
1. 如果当前节点被访问过,则返回隆图中对应节点
296-
2. 如果当前节点没有被访问过,则创建一个新的节点,并保存在哈希表中
297-
3. 遍历当前节点的邻接节点列表,递归调用当前节点的邻接节点,并将其放入克隆图中对应节点
298-
3. 递归结束,返回克隆节点
316+
1. 使用哈希表 $visitedDict$ 记录原图节点与其对应的克隆节点,键为原图节点,值为克隆节点
317+
2. 从给定节点出发,采用深度优先搜索遍历原图:
318+
1. 如果当前节点已在哈希表中,直接返回对应的克隆节点,避免重复克隆
319+
2. 如果当前节点未被访问,则新建一个克隆节点,并将其加入哈希表
320+
3. 遍历当前节点的所有邻居,对每个邻居递归调用克隆函数,并将返回的克隆节点加入当前克隆节点的邻居列表
321+
3. 递归返回当前节点的克隆节点
299322

300323
##### 思路 1:代码
301324

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /