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 6763734

Browse files
committed
Update 03.Graph-BFS.md
1 parent 7bb6f0b commit 6763734

File tree

1 file changed

+111
-66
lines changed

1 file changed

+111
-66
lines changed

‎Contents/08.Graph/02.Graph-Traversal/03.Graph-BFS.md‎

Lines changed: 111 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
## 1. 广度优先搜索简介
42

53
> **广度优先搜索算法**(Breadth First Search):简称为 BFS,又译作宽度优先搜索 / 横向优先搜索。是一种用于遍历或搜索树或图的算法。该算法从根节点开始,沿着树的宽度遍历树或图的节点。如果所有节点均被访问,则算法中止。
@@ -24,110 +22,137 @@ graph = {
2422
}
2523
```
2624

27-
该无向图的结构如图左所示,图右为宽度优先搜索的遍历路径。
25+
该无向图对应的邻接字典表示:无向图中有 `A``B``C``D``E``F``6` 个节点,其中与 `A` 节点相连的有 `B``C` 两个节点,与 `B` 节点相连的有 `A``C``D` 三个节点,等等。
26+
27+
该无向图的结构如图左所示,其宽度优先搜索的遍历路径如图右所示。
2828

2929
![](https://qcdn.itcharge.cn/images/20220105135253.png)
3030

31-
其对应的动态演示图如下所示
31+
其广度优先搜索的遍历过程如下动图所示
3232

3333
![](https://qcdn.itcharge.cn/images/20220105175535.gif)
3434

3535
## 3. 基于队列实现的广度优先搜索
3636

3737
### 3.1 基于队列实现的广度优先搜索实现步骤
3838

39-
1. `graph` 为存储无向图的字典变量,`start` 为开始节点。
40-
1. 然后定义 `visited` 为标记访问节点的 set 集合变量。定义 `q` 为存放节点的队列。
41-
1. 首先将起始节点放入队列 `q`,即 `q.put(start)`并将其标记为访问,即 `visited.add(start)`
39+
1. 定义 `graph` 为存储无向图的字典变量,`start` 为开始节点,`def bfs(graph, start):` 为队列实现的广度优先搜索方法
40+
2. 定义 `visited` 为标记访问节点的 set 集合变量,`queue` 为存放节点的队列。
41+
3. 首先将起始节点标记为访问,即 `visited.add(start)`并将其放入队列 `queue`,即 `queue.append(start)`
4242
4. 从队列中取出第一个节点 `node_u`。访问节点 `node_u`,并对节点进行相关操作(看具体题目要求)。
43-
2. 遍历与节点 `node_u` 相连并构成边的节点 `node_v`
44-
- 如果 `node_v` 没有被访问过,则将 `node_v` 节点放入队列中,并标记访问,即 `q.append(node_v)`,`visited.add(node_v)`
45-
6. 重复步骤 4 ~ 5,直到 `q` 为空。
43+
5. 遍历与节点 `node_u` 相连并构成边的节点 `node_v`
44+
- 如果 `node_v` 没有被访问过(即 `node_v` 不在 `visited` 中):则将 `node_v` 节点放入队列中,并标记访问,即 `q.append(node_v)`,`visited.add(node_v)`
45+
6. 重复步骤 4 ~ 5,直到队列 `queue` 为空。
4646

4747
### 3.2 基于队列实现的广度优先搜索实现代码
4848

4949
```Python
5050
import collections
5151

5252
def bfs(graph, start):
53-
visited = set(start)
54-
q = collections.deque([start])
53+
visited = set()
54+
queue = collections.deque([])
55+
56+
visited.add(start)
57+
queue.append(start)
5558

56-
while q:
57-
node_u = q.popleft()
59+
while queue:
60+
node_u = queue.popleft()
5861
print(node_u)
5962
for node_v in graph[node_u]:
6063
if node_v not in visited:
6164
visited.add(node_v)
62-
q.append(node_v)
65+
queue.append(node_v)
6366
```
6467

6568
## 4. 广度优先搜索应用
6669

67-
### 4.1 无向图中连通分量的数目
70+
### 4.1 克隆图
6871

6972
#### 4.1.1 题目链接
7073

71-
- [323. 无向图中连通分量的数目 - 力扣(LeetCode)](https://leetcode.cn/problems/number-of-connected-components-in-an-undirected-graph/)
74+
- [133. 克隆图 - 力扣(LeetCode)](https://leetcode.cn/problems/clone-graph/)
7275

7376
#### 4.1.2 题目大意
7477

75-
给定 `n` 个节点(编号从 `0``n - 1`)的图的无向边列表 `edges`,其中 `edges[i] = [u, v]` 表示节点 `u` 和节点 `v` 之间有一条无向边
78+
**描述**:以每个节点的邻接列表形式(二维列表)给定一个无向连通图,其中 `adjList[i]` 表示值为 `i + 1`的节点的邻接列表,`adjList[i][j]` 表示值为 `i + 1` 的节点与值为 `adjList[i][j]` 的节点有一条边
7679

77-
要求:计算该无向图中连通分量的数量
80+
**要求**:返回该图的深拷贝
7881

79-
#### 4.1.3 解题思路
82+
**说明**:
8083

81-
先来看一下图论中相关的名次解释。
84+
- 节点数不超过 `100`
85+
- 每个节点值 $Node.val$ 都是唯一的,1ドル \le Node.val \le 100$。
86+
- 无向图是一个简单图,这意味着图中没有重复的边,也没有自环。
87+
- 由于图是无向的,如果节点 `p` 是节点 `q` 的邻居,那么节点 `q` 也必须是节点 `p` 的邻居。
88+
- 图是连通图,你可以从给定节点访问到所有节点。
8289

83-
- 连通图:在无向图中,如果可以从顶点 $v_i$ 到达 $v_j,ドル则称 $v_i$ 和 $v_j$ 连通。如果图中任意两个顶点之间都连通,则称该图为连通图。
84-
- 无向图的连通分量:如果该图为连通图,则连通分量为本身;否则将无向图中的极大连通子图称为连通分量,每个连通分量都是一个连通图。
85-
- 无向图的连通分量个数:无向图的极大连通子图的个数。
90+
**示例**:
8691

87-
接下来我们来解决这道题。
92+
![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/02/01/133_clone_graph_question.png)
8893

89-
由于题目给定的是无向边列表。我们先将其转变为邻接表的形式。然后使用广度优先搜索计算联通分量的个数。具体做法如下:
94+
```Python
95+
输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
96+
输出:[[2,4],[1,3],[2,4],[1,3]]
97+
解释:
98+
图中有 4 个节点。
99+
节点 1 的值是 1,它有两个邻居:节点 24
100+
节点 2 的值是 2,它有两个邻居:节点 13
101+
节点 3 的值是 3,它有两个邻居:节点 24
102+
节点 4 的值是 4,它有两个邻居:节点 13
103+
```
90104

91-
- 使用变量 `count` 记录连通分量个数。使用集合变量 `visited` 记录访问过的节点,使用邻接表 `graph` 记录图结构。
92-
-`0` 开始,依次遍历 `n` 个节点。
93-
- 如果第 `i` 个节点未访问过:
94-
- 将其添加到 `visited` 中。
95-
- 并且连通分量个数累加,即 `count += 1`
96-
- 定义一个队列 `q`,将第 `i` 个节点加入到队列中。
97-
- 从队列中取出第一个节点,遍历与其链接的节点,并将未遍历过的节点加入到队列 `q``visited` 中。
98-
- 直到队列为空,则继续向后遍历。
105+
![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/02/01/graph-1.png)
99106

100-
- 最后输出连通分量数目 `count`
107+
```Python
108+
输入:adjList = [[2],[1]]
109+
输出:[[2],[1]]
110+
```
111+
112+
#### 4.1.3 解题思路
101113

102-
####4.1.4 代码
114+
##### 思路 1:广度优先搜索
103115

104-
```Python
105-
import collections
116+
1. 使用哈希表 `visited` 来存储原图中被访问过的节点和克隆图中对应节点,键值对为 原图被访问过的节点:克隆图中对应节点。使用队列`queue` 存放节点。
117+
2. 根据起始节点,创建一个新的节点,并将其添加到哈希表 `visited` 中,即 `visited[node] = Node(node.val, [])`。然后将起始节点放入队列 `queue`中,即 `queue.append(node)`
118+
3. 从队列中取出第一个节点 `node_u`。访问节点 `node_u`
119+
4. 遍历与节点 `node_u` 相连并构成边的节点 `node_v`
120+
1. 如果 `node_v` 没有被访问过(即 `node_v` 不在 `visited` 中):
121+
1. 则根据 `node_v` 创建一个新的节点,并将其添加到哈希表 `visited` 中,即 `visited[node_v] = Node(node_v.val, [])`
122+
2. 然后将 `node_v` 节点放入队列 `queue` 中,即 `queue.append(node_v)`
123+
5. 重复步骤 3 ~ 4,直到队列 `queue` 为空。
124+
6. 广度优先搜索结束,返回起始节点的克隆节点(即 `visited[node]`)。
106125

126+
##### 思路 1:代码
127+
128+
```Python
107129
class Solution:
108-
def countComponents(self, n: int, edges: List[List[int]]) -> int:
109-
count = 0
110-
visited = set()
111-
graph = [[] for _ in range(n)]
112-
113-
for x, y in edges:
114-
graph[x].append(y)
115-
graph[y].append(x)
116-
117-
for i in range(n):
118-
if i not in visited:
119-
visited.add(i)
120-
count += 1
121-
q = collections.deque([i])
122-
while q:
123-
node_u = q.popleft()
124-
for node_v in graph[node_u]:
125-
if node_v not in visited:
126-
visited.add(node_v)
127-
q.append(node_v)
128-
return count
130+
def cloneGraph(self, node: 'Node') -> 'Node':
131+
if not node:
132+
return node
133+
134+
visited = dict()
135+
queue = collections.deque()
136+
137+
visited[node] = Node(node.val, [])
138+
queue.append(node)
139+
140+
while queue:
141+
node_u = queue.popleft()
142+
for node_v in node_u.neighbors:
143+
if node_v not in visited:
144+
visited[node_v] = Node(node_v.val, [])
145+
queue.append(node_v)
146+
visited[node_u].neighbors.append(visited[node_v])
147+
148+
return visited[node]
129149
```
130150

151+
##### 思路 1:复杂度分析
152+
153+
- **时间复杂度**:$O(n)$。其中 $n$ 为图中节点数量。
154+
- **空间复杂度**:$O(n)$。
155+
131156
### 4.2 岛屿的最大面积
132157

133158
#### 4.2.1 题目链接
@@ -136,19 +161,34 @@ class Solution:
136161

137162
#### 4.2.2 题目大意
138163

139-
给定一个只包含 `0``1` 元素的二维数组 `grid`,其中 `1` 代表岛屿,`0` 代表水。一座岛的面积就是上下左右相邻的 `1` 所组成的连通块的数目。
164+
**描述**:给定一个只包含 `0``1` 元素的二维数组,`1` 代表岛屿,`0` 代表水。一座岛的面积就是上下左右相邻的 `1` 所组成的连通块的数目。
165+
166+
**要求**:计算出最大的岛屿面积。
167+
168+
**说明**:
140169

141-
要求:计算出最大的岛屿面积。
170+
- $m == grid.length$。
171+
- $n == grid[i].length$。
172+
- 1ドル \le m, n \le 50$。
173+
- $grid[i][j]$ 为 `0``1`
142174

143-
示例 :
175+
**示例**:
144176

145-
![img](https://assets.leetcode.com/uploads/2021/05/01/maxarea1-grid.jpg)
177+
![](https://assets.leetcode.com/uploads/2021/05/01/maxarea1-grid.jpg)
146178

147-
图中最大的岛屿面积为 6。
179+
```Python
180+
输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
181+
输出:6
182+
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1
183+
184+
185+
输入:grid = [[0,0,0,0,0,0,0,0]]
186+
输出:0
187+
```
148188

149189
#### 4.2.3 解题思路
150190

151-
使用广度优先搜索方法。具体做法如下:
191+
##### 思路 1:广度优先搜索
152192

153193
1. 使用 `ans` 记录最大岛屿面积。
154194
2. 遍历二维数组的每一个元素,对于每个值为 `1` 的元素:
@@ -157,7 +197,7 @@ class Solution:
157197
3. 不断重复上一步骤,直到队列 `q` 为空。
158198
4. 更新当前最大岛屿面积,即 `ans = max(ans, temp_ans)`
159199

160-
####4.2.4 代码
200+
##### 思路 1:代码
161201

162202
```Python
163203
import collections
@@ -188,6 +228,11 @@ class Solution:
188228
return ans
189229
```
190230

231+
##### 思路 1:复杂度分析
232+
233+
- **时间复杂度**:$O(n \times m),ドル其中 $m$ 和 $n$ 分别为行数和列数。
234+
- **空间复杂度**:$O(n \times m)$。
235+
191236
## 参考资料
192237

193238
- 【文章】[广度优先搜索 - LeetBook - 力扣(LeetCode)](https://leetcode.cn/leetbook/read/bfs/e69rh1/)

0 commit comments

Comments
(0)

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