diff --git a/Contents/08.Graph/04.Graph-Shortest-Path/01.Graph-Single-Source-Shortest-Path-01.md b/Contents/08.Graph/04.Graph-Shortest-Path/01.Graph-Single-Source-Shortest-Path-01.md index 6152ecb6..acb980ce 100644 --- a/Contents/08.Graph/04.Graph-Shortest-Path/01.Graph-Single-Source-Shortest-Path-01.md +++ b/Contents/08.Graph/04.Graph-Shortest-Path/01.Graph-Single-Source-Shortest-Path-01.md @@ -21,33 +21,561 @@ > **Dijkstra 算法的算法思想**:通过逐步选择距离起始节点最近的节点,并根据这些节点的路径更新其他节点的距离,从而逐步找到最短路径。 ### 2.2 Dijkstra 算法的实现步骤 +#### **1. 初始化** +- **输入**:图的表示(邻接矩阵或边集数组)、起始节点。 +- **输出**:从起始节点到其他所有节点的最短距离。 +- **初始化步骤**: + 1. 创建一个距离数组 `distance`,初始值为无穷大(`float('inf')`),表示从起始节点到其他节点的距离。 + 2. 将起始节点的距离设为 0。 + 3. 创建一个布尔数组 `visited`,用于记录节点是否已被访问。 + 4. 创建一个优先队列(或列表),初始时包含起始节点及其距离。 + +#### **2. 选择当前距离最小的节点** +- 从优先队列中选择距离最小的节点。 +- 如果队列为空或所有节点都已访问,算法结束。 + +#### **3. 标记节点为已访问** +- 将当前节点标记为已访问(`visited[current_node] = True`)。 + +#### **4. 遍历当前节点的邻居** +- 对于当前节点的每个邻居: + 1. 如果邻居节点未被访问: + - 计算从起始节点到邻居节点的新距离(`current_dist + weight`)。 + - 如果新距离小于当前记录的距离,更新距离并将其加入优先队列。 + +#### **5. 重复步骤 2-4** +- 重复上述步骤,直到所有节点都被访问或队列为空。 ### 2.3 Dijkstra 算法的实现代码 +1. **邻接矩阵** + +```python +def dijkstra_adjacency_matrix(matrix, start_node): + """ + 使用邻接矩阵实现 Dijkstra 算法,不使用 heapq 包。 + + 参数: + matrix: 邻接矩阵,表示图的结构。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + """ + # 获取图中节点的数量 + n = len(matrix) + + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * n + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 记录节点是否已被访问 + visited = [False] * n + + # 优先队列,存储 (距离, 节点),初始时只有起始节点 + priority_queue = [(0, start_node)] + + while priority_queue: + # 手动找到优先队列中距离最小的节点 + min_dist = float('inf') + min_index = -1 + + for i, (current_dist, node) in enumerate(priority_queue): + if current_dist < min_dist and not visited[node]: + min_dist = current_dist + min_index = i + + # 如果队列为空或所有节点都已访问,退出循环 + if min_index == -1: + break + + # 弹出距离最小的节点 + current_dist, current_node = priority_queue.pop(min_index) + + # 如果该节点已被访问过,跳过 + if visited[current_node]: + continue + + # 标记该节点为已访问 + visited[current_node] = True + + # 遍历当前节点的所有邻居 + for neighbor in range(n): + # 如果邻居节点未被访问且存在边 + if not visited[neighbor] and matrix[current_node][neighbor] != 0: + # 计算新的距离 + new_dist = current_dist + matrix[current_node][neighbor] + + # 如果新距离更小,更新距离并加入优先队列 + if new_dist < distance[neighbor]: + distance[neighbor] = new_dist + # 将新的距离和节点加入优先队列 + priority_queue.append((new_dist, neighbor)) + + return distance + +# 示例 +if __name__ == "__main__": + # 邻接矩阵表示图 + # 0: 起点,1-4: 其他节点 + matrix = [ + [0, 4, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [0, 0, 0, 5, 10, 0], + [0, 0, 0, 0, 3, 0], + [0, 0, 0, 0, 0, 5], + [0, 0, 0, 0, 0, 0] + ] + + start_node = 0 + shortest_distances = dijkstra_adjacency_matrix(matrix, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(shortest_distances): + print("到节点 {}: 距离 = {}".format(i, dist)) +``` +2. **边集数组** ```python +def dijkstra_edge_list(edges, num_nodes, start_node): + """ + 使用边集数组实现 Dijkstra 算法,不使用 heapq 包。 + + 参数: + edges: 边集数组,每条边表示为 (起点, 终点, 权重)。 + num_nodes: 图中节点的数量。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + """ + # 构建邻接表 + adjacency_list = [[] for _ in range(num_nodes)] + for u, v, weight in edges: + adjacency_list[u].append((v, weight)) + + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * num_nodes + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 记录节点是否已被访问 + visited = [False] * num_nodes + + # 优先队列,存储 (距离, 节点),初始时只有起始节点 + priority_queue = [(0, start_node)] + + while priority_queue: + # 手动找到优先队列中距离最小的节点 + min_dist = float('inf') + min_index = -1 + + for i, (current_dist, node) in enumerate(priority_queue): + if current_dist < min_dist and not visited[node]: + min_dist = current_dist + min_index = i + + # 如果队列为空或所有节点都已访问,退出循环 + if min_index == -1: + break + + # 弹出距离最小的节点 + current_dist, current_node = priority_queue.pop(min_index) + + # 如果该节点已被访问过,跳过 + if visited[current_node]: + continue + + # 标记该节点为已访问 + visited[current_node] = True + + # 遍历当前节点的所有邻居 + for neighbor, weight in adjacency_list[current_node]: + # 如果邻居节点未被访问 + if not visited[neighbor]: + # 计算新的距离 + new_dist = current_dist + weight + + # 如果新距离更小,更新距离并加入优先队列 + if new_dist < distance[neighbor]: + distance[neighbor] = new_dist + # 将新的距离和节点加入优先队列 + priority_queue.append((new_dist, neighbor)) + + return distance +# 示例 +if __name__ == "__main__": + # 边集数组表示图 + edges = [ + (0, 1, 4), + (1, 2, 2), + (2, 3, 5), + (2, 4, 10), + (3, 4, 3), + (4, 5, 5) + ] + + num_nodes = 6 + start_node = 0 + shortest_distances = dijkstra_edge_list(edges, num_nodes, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(shortest_distances): + print("到节点 {}: 距离 = {}".format(i, dist)) ``` ## 3. Bellman-Ford 算法 ### 3.1 Bellman-Ford 算法的算法思想 +> **Bellman-Ford 算法的算法思想**:通过逐步松弛图中的所有边,逐步优化路径长度,从而找到从起始节点到其他所有节点的最短路径。算法能够处理负权边,并通过额外的检测步骤发现图中是否存在负权环。 ### 3.2 Bellman-Ford 算法的实现步骤 +#### **1. 初始化** +- **输入**:图的表示(邻接矩阵或边集数组)、起始节点。 +- **输出**:从起始节点到其他所有节点的最短距离。 +- **初始化步骤**: + 1. 创建一个距离数组 `distance`,初始值为无穷大(`float('inf')`),表示从起始节点到其他节点的距离。 + 2. 将起始节点的距离设为 0。 + 3. 创建一个前驱节点数组 `predecessor`,用于记录每个节点的前驱节点,帮助重建路径。 + +#### **2. 松弛操作** +- **松弛次数**:进行 `num_nodes-1` 次松弛操作,其中 `num_nodes` 是图中节点的数量。 +- **松弛过程**: + 1. 遍历图中的所有边 `(u, v, weight)`。 + 2. 对于每条边,检查从起始节点到 `v` 的当前距离是否大于从起始节点到 `u` 的距离加上 `u` 到 `v` 的权重。 + 3. 如果是,则更新 `v` 的距离和前驱节点。 + +#### **3. 负权环检测** +- 在松弛操作完成后,再次遍历所有边。 +- 如果还能找到可以更新的距离,则说明图中存在负权环,无法计算最短路径。 + +#### **4. 输出结果** +- 如果没有负权环,则输出从起始节点到其他所有节点的最短距离和前驱节点数组。 + ### 3.3 Bellman-Ford 算法的实现代码 +1. **邻接矩阵** +```python +def bellman_ford_adjacency_matrix(matrix, start_node): + """ + 使用邻接矩阵实现 Bellman-Ford 算法。 + + 参数: + matrix: 邻接矩阵,表示图的结构。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + predecessor: 每个节点的前驱节点,用于重建路径。 + """ + # 获取图中节点的数量 + n = len(matrix) + + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * n + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 初始化前驱节点数组 + predecessor = [None] * n + + # 进行 n-1 次松弛操作 + for _ in range(n - 1): + # 遍历所有可能的边 (i, j) + for i in range(n): + for j in range(n): + # 如果存在边 i -> j 且距离可以更新 + if matrix[i][j] != 0 and distance[i] != float('inf'): + new_dist = distance[i] + matrix[i][j] + if new_dist < distance[j]: + distance[j] = new_dist + predecessor[j] = i + + # 检查是否存在负权环 + for i in range(n): + for j in range(n): + if matrix[i][j] != 0 and distance[i] != float('inf'): + if distance[j] > distance[i] + matrix[i][j]: + raise ValueError("图中存在负权环,无法计算最短路径") + + return distance, predecessor + +# 示例 +if __name__ == "__main__": + # 邻接矩阵表示图 + # 0: 起点,1-4: 其他节点 + matrix = [ + [0, 4, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [0, 0, 0, 5, 10, 0], + [0, 0, 0, 0, 3, 0], + [0, 0, 0, 0, 0, 5], + [0, 0, 0, 0, 0, 0] + ] + + start_node = 0 + distance, predecessor = bellman_ford_adjacency_matrix(matrix, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(distance): + print("到节点 {}: 距离 = {}".format(i, dist)) + + print("\n前驱节点数组:") + for i, pred in enumerate(predecessor): + print("节点 {} 的前驱节点:{}".format(i, pred)) + +``` +2. **边集数组** ```python +def bellman_ford_edge_list(edges, num_nodes, start_node): + """ + 使用边集数组实现 Bellman-Ford 算法。 + + 参数: + edges: 边集数组,每条边表示为 (起点, 终点, 权重)。 + num_nodes: 图中节点的数量。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + predecessor: 每个节点的前驱节点,用于重建路径。 + """ + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * num_nodes + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 初始化前驱节点数组 + predecessor = [None] * num_nodes + + # 进行 num_nodes-1 次松弛操作 + for _ in range(num_nodes - 1): + # 遍历所有边 + for u, v, weight in edges: + # 如果存在更短的路径 + if distance[u] != float('inf') and distance[v] > distance[u] + weight: + distance[v] = distance[u] + weight + predecessor[v] = u + + # 检查是否存在负权环 + for u, v, weight in edges: + if distance[u] != float('inf') and distance[v] > distance[u] + weight: + raise ValueError("图中存在负权环,无法计算最短路径") + + return distance, predecessor +# 示例 +if __name__ == "__main__": + # 边集数组表示图 + edges = [ + (0, 1, 4), + (1, 2, 2), + (2, 3, 5), + (2, 4, 10), + (3, 4, 3), + (4, 5, 5) + ] + + num_nodes = 6 + start_node = 0 + distance, predecessor = bellman_ford_edge_list(edges, num_nodes, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(distance): + print("到节点 {}: 距离 = {}".format(i, dist)) + + print("\n前驱节点数组:") + for i, pred in enumerate(predecessor): + print("节点 {} 的前驱节点:{}".format(i, pred)) ``` ## 4. SPFA 算法 - ### 4.1 SPFA 算法的算法思想 -### 4.2 SPFA 算法的实现步骤 +> **SPFA 算法的算法思想**:SPFA(Shortest Path Faster Algorithm)是对 Bellman-Ford 算法的优化,通过使用队列和距离更新的机制来减少不必要的松弛操作。其核心思想是利用一个先进先出的队列来存储需要松弛的节点,并通过距离更新来动态调整队列中的节点顺序,从而加速最短路径的计算。SPFA 算法特别适用于稀疏图,能够显著提高效率。 + +### 4.2 **SPFA 算法的实现步骤** + +#### **1. 初始化** +- **输入**:图的表示(邻接表)、起始节点。 +- **输出**:从起始节点到其他所有节点的最短距离。 +- **初始化步骤**: + 1. 创建一个距离数组 `distance`,初始值为无穷大(`float('inf')`),表示从起始节点到其他节点的距离。 + 2. 将起始节点的距离设为 0。 + 3. 创建一个队列,初始时包含起始节点。 + 4. 创建一个布尔数组 `in_queue`,用于记录节点是否在队列中,避免重复入队。 + +#### **2. 松弛操作** +- **队列处理**:使用一个队列来存储需要松弛的节点。 +- **松弛过程**: + 1. 从队列中取出一个节点 `u`。 + 2. 遍历 `u` 的所有邻居节点 `v`。 + 3. 对于每个邻居节点 `v`,计算从起始节点到 `v` 的新距离:`new_dist = distance[u] + weight`。 + 4. 如果 `new_dist < distance[v]`,更新 `distance[v]`,并将 `v` 入队(如果 `v` 不在队列中)。 + +#### **3. 负权环检测** +- 在松弛过程中,如果某个节点被多次入队(超过节点总数次),则说明图中存在负权环。 + +#### **4. 输出结果** +- 如果没有负权环,则输出从起始节点到其他所有节点的最短距离。 ### 4.3 SPFA 算法的实现代码 +1. **邻接矩阵表示** +```python +from collections import deque + +def spfa_adjacency_matrix(matrix, start_node): + """ + 使用邻接矩阵实现 SPFA 算法。 + + 参数: + matrix: 邻接矩阵,表示图的结构。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + """ + # 获取图中节点的数量 + n = len(matrix) + + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * n + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 记录节点是否在队列中 + in_queue = [False] * n + + # 记录每个节点的入队次数,用于检测负权环 + cnt = [0] * n + + # 创建队列并加入起始节点 + queue = deque() + queue.append(start_node) + in_queue[start_node] = True + cnt[start_node] += 1 + + while queue: + u = queue.popleft() + in_queue[u] = False + + # 遍历当前节点的所有邻居 + for v in range(n): + if matrix[u][v] != 0: # 如果存在边 u -> v + if distance[v] > distance[u] + matrix[u][v]: + distance[v] = distance[u] + matrix[u][v] + if not in_queue[v]: + queue.append(v) + in_queue[v] = True + cnt[v] += 1 + # 如果入队次数超过节点数,说明存在负权环 + if cnt[v] > n: + raise ValueError("图中存在负权环,无法计算最短路径") + + return distance +# 示例 +if __name__ == "__main__": + # 邻接矩阵表示图 + # 0: 起点,1-5: 其他节点 + matrix = [ + [0, 4, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [0, 0, 0, 5, 10, 0], + [0, 0, 0, 0, 3, 0], + [0, 0, 0, 0, 0, 5], + [0, 0, 0, 0, 0, 0] + ] + + start_node = 0 + distance = spfa_adjacency_matrix(matrix, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(distance): + print("到节点 {}: 距离 = {}".format(i, dist)) +``` +2. **边集数组表示** ```python +from collections import deque + +def spfa_edge_list(edges, num_nodes, start_node): + """ + 使用边集数组实现 SPFA 算法。 + + 参数: + edges: 边集数组,每条边表示为 (起点, 终点, 权重)。 + num_nodes: 图中节点的数量。 + start_node: 起始节点的索引。 + + 返回: + distance: 从起始节点到其他所有节点的最短距离。 + """ + # 构建邻接表 + adjacency_list = [[] for _ in range(num_nodes)] + for u, v, weight in edges: + adjacency_list[u].append((v, weight)) + + # 初始化距离数组,初始值为无穷大 + distance = [float('inf')] * num_nodes + + # 起始节点的距离为 0 + distance[start_node] = 0 + + # 记录节点是否在队列中 + in_queue = [False] * num_nodes + + # 记录每个节点的入队次数,用于检测负权环 + cnt = [0] * num_nodes + + # 创建队列并加入起始节点 + queue = deque() + queue.append(start_node) + in_queue[start_node] = True + cnt[start_node] += 1 + + while queue: + u = queue.popleft() + in_queue[u] = False + + # 遍历当前节点的所有邻居 + for v, weight in adjacency_list[u]: + if distance[v] > distance[u] + weight: + distance[v] = distance[u] + weight + if not in_queue[v]: + queue.append(v) + in_queue[v] = True + cnt[v] += 1 + # 如果入队次数超过节点数,说明存在负权环 + if cnt[v] > num_nodes: + raise ValueError("图中存在负权环,无法计算最短路径") + + return distance + +# 示例 +if __name__ == "__main__": + # 边集数组表示图 + edges = [ + (0, 1, 4), + (1, 2, 2), + (2, 3, 5), + (2, 4, 10), + (3, 4, 3), + (4, 5, 5) + ] + + num_nodes = 6 + start_node = 0 + distance = spfa_edge_list(edges, num_nodes, start_node) + + print("从节点 {} 到其他节点的最短距离:".format(start_node)) + for i, dist in enumerate(distance): + print("到节点 {}: 距离 = {}".format(i, dist)) ``` diff --git "a/Solutions/0407. \346\216\245\351\233\250\346\260\264II.md" "b/Solutions/0407. \346\216\245\351\233\250\346\260\264II.md" new file mode 100644 index 00000000..c1f8b955 --- /dev/null +++ "b/Solutions/0407. \346\216\245\351\233\250\346\260\264II.md" @@ -0,0 +1,128 @@ +# [0407. 接雨水II](https://leetcode.cn/problems/trapping-rain-water-ii/) + +- 标签:广度优先搜索、数组、矩阵、堆(优先队列) +- 难度:困难 +## 题目链接 + +- [0407. 接雨水II](https://leetcode.cn/problems/trapping-rain-water-ii/) + +## 题目大意 + +**描述**:给你一个 $m * n$ 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度。 + +**要求**:计算图中形状最多能接多少体积的雨水。 + +**示例**: +- 示例1 +```python +输入: heightMap = [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] +输出: 4 +解释: 下雨后,雨水将会被上图蓝色的方块中。总的接雨水量为1+2+1=4。 +``` +## 解题思路 + +### 思路 1:最小堆 + +本题为经典题目,解题的原理和方法都可以参考[0042.接雨水](https://leetcode.cn/problems/trapping-rain-water/),本题主要从一维数组变成了二维数组。 +首先考虑什么样的方块一定可以接住水: + +- 该方块不为最外层的方块; +- 该方块自身的高度比其上下左右四个相邻的方块接水后的高度都要低; +假设方块的索引为 $(i,j)$,方块的高度为 $heightMap[i][j]$,方块接水后的高度为 $water[i][j]$。则方块 $(i,j)$ 的接水后的高度为: +$ +water[i][j]=max(heightMap[i][j],min(water[i−1][j],water[i+1][j],water[i][j−1],water[i][j+1])) +$ +方块 (i,j) 实际接水的容量计算公式为 $water[i][j]−heightMap[i][j]$。 +可以确定的是,矩阵的最外层的方块接水后的高度就是方块的自身高度,因为最外层的方块无法接水,因此最外层的方块 $water[i][j]=heightMap[i][j]$。 +根据木桶原理,接到的雨水的高度由这个容器周围最短的木板来确定的。容器内水的高度取决于最外层高度最低的方块,假设最外层的方块接水后的高度的最小值,则此时根据木桶原理,肯定可以确定最小高度方块的相邻方块的接水高度。同时更新最外层的方块标记,在新的最外层的方块再次找到接水后的高度的最小值,同时确定与其相邻的方块的接水高度,然后再次更新最外层,依次迭代直到求出所有的方块的接水高度,即可知道矩阵中的接水容量。 + + + + +### 思路 1:代码 + +```python +import heapq +class Solution: + def trapRainWater(self, heightMap: List[List[int]]) -> int: + if len(heightMap) <= 2 or len(heightMap[0]) <= 2: + return 0 + + m, n = len(heightMap), len(heightMap[0]) + visited = [[0 for _ in range(n)] for _ in range(m)] + pq = [] + for i in range(m): + for j in range(n): + if i == 0 or i == m - 1 or j == 0 or j == n - 1: + visited[i][j] = 1 + heapq.heappush(pq, (heightMap[i][j], i * n + j)) + + res = 0 + dirs = [-1, 0, 1, 0, -1] + while pq: + height, position = heapq.heappop(pq) + for k in range(4): + nx, ny = position // n + dirs[k], position % n + dirs[k + 1] + if nx >= 0 and nx < m and ny >= 0 and ny < n and visited[nx][ny] == 0: + if height > heightMap[nx][ny]: + res += height - heightMap[nx][ny] + visited[nx][ny] = 1 + heapq.heappush(pq, (max(height, heightMap[nx][ny]), nx * n + ny)) + return res + +``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(mn \times \log mn)$。 +- **空间复杂度**:$O(mn)$。 + +### 思路 2:广度优先搜索 + +假设初始时矩阵的每个格子都接满了水,且高度均为 $maxHeight$,其中 $maxHeight$ 为矩阵中高度最高的格子。方块接水后的高度为 $water[i][j]$,它的求解公式与思路1一样。方块 (i,j) 的接水后的高度为: +$water[i][j]=max(heightMap[i][j],min(water[i−1][j],water[i+1][j],water[i][j−1],water[i][j+1]))$ +方块 $(i,j)$ 实际接水的容量计算公式为 $water[i][j]−heightMap[i][j]$。 +首先假设每个方块 $(i,j)$ 的接水后的高度均为 $water[i][j]=maxHeight$,最外层的方块的肯定不能接水,所有的多余的水都会从最外层的方块溢出,当前方块 $(i,j)$ 的接水高度 $water[i][j]$ 小于与它相邻的 4 个模块的接水高度时,将进行调整接水高度,将其相邻的四个方块的接水高度调整与 $(i,j)$ 的高度保持一致,不断重复的进行调整,直到所有的方块的接水高度不再有调整时即为满足要求。 + +### 思路 2:代码 + +```python +class Solution: + def trapRainWater(self, heightMap: List[List[int]]) -> int: + m, n = len(heightMap), len(heightMap[0]) + maxHeight = max(max(row) for row in heightMap) + water = [[maxHeight for _ in range(n)] for _ in range(m)] + dirs = [-1, 0, 1, 0, -1] + + qu = [] + for i in range(m): + for j in range(n): + if i == 0 or i == m - 1 or j == 0 or j == n - 1: + if water[i][j] > heightMap[i][j]: + water[i][j] = heightMap[i][j] + qu.append([i, j]) + + while len(qu) > 0: + [x, y] = qu.pop(0) + for i in range(4): + nx, ny = x + dirs[i], y + dirs[i + 1] + if nx < 0 or nx >= m or ny < 0 or ny >= n: + continue + if water[x][y] < water[nx][ny] and water[nx][ny] > heightMap[nx][ny]: + water[nx][ny] = max(water[x][y], heightMap[nx][ny]) + qu.append([nx, ny]) + + ans = 0 + for i in range(m): + for j in range(n): + ans = ans + water[i][j] - heightMap[i][j] + return ans +``` +### 思路 2:复杂度分析 + +- **时间复杂度**:$O(m^2 n^2)$。 +- **空间复杂度**:$O(mn)$。 + + + + +