797. 所有可能的路径
给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)
graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。
n == graph.length
2 <= n <= 15
0 <= graph[i][j] < n
graph[i][j] != i(即不存在自环)
graph[i] 中的所有元素 互不相同
保证输入为 有向无环图(DAG)
本题是我入门DFS的第一个题目。
所谓DFS是深度优先搜索,即沿着一个方向不停搜索,直至搜不下去以后,再换方向。
正是因为DFS搜索可一个方向,并需要回溯,所以用递归的方式来实现是最方便的.Python代码框架如下:
def dfs(参数) :
if 终止条件:
存放结果
return
for 本节点所连接的其他节点:
处理节点
dfs(图,选择的节点) # 递归
回溯,撤销处理结果
参考卡哥给出的深搜三部曲:
- 首先需要确定递归函数&参数
def dfs(self, graph, root: int):
此处的graph为题目给出的图,root为当前搜索的根节点
-
确认终止条件:
当目前遍历的节点 为 最后一个节点的时候,就找到了一条,从 出发点到终止点的路径。即root==len(graph)-1,来到最后一个节点的时候,path的结果可以被添加到result中来。并返回。 -
处理目前搜索节点出发的路径,遍历每一个节点然后回溯
for node in graph[root]:
path.append(node)
dfs(graph, node)
path.pop() # 回溯
完整Python代码如下:
class Solution:
def __init__(self):
self.result = []
self.path = [0]
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
if not graph: return []
self.dfs(graph, 0)
return self.result
def dfs(self, graph, root: int):
if root == len(graph) - 1: # 成功找到一条路径时
# ***Python的list是mutable类型***
# ***回溯中必须使用Deep Copy***
self.result.append(self.path[:])
return
for node in graph[root]: # 遍历节点n的所有后序节点
self.path.append(node)
self.dfs(graph, node)
self.path.pop() # 回溯
200. 岛屿数量(连通性)
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’
本题思路,是用遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上。
在遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。
那么如果把节点陆地所能遍历到的陆地都标记上呢,就可以使用 DFS,BFS或者并查集。
本文我们先学习DFS。那么如何在二维矩阵中使用 DFS 搜索呢?如果把二维矩阵中的每一个位置看做一个节点,这个节点的上下左右四个位置就是相邻节点,那么整个矩阵就可以抽象成一幅网状的「图」结构,使用visited数组来记录节点的访问情况。
用二叉树遍历的思路,学习二维DFS的框架
"""
仿照二叉树的遍历框架改写出二维矩阵的 DFS 代码框架
"""
# 二维矩阵遍历框架
def dfs(grid, i, j, visited):
m, n = len(grid), len(grid[0])
if i < 0 or j < 0 or i >= m or j >= n:
# 超出索引边界
return
if visited[i][j]:
# 已遍历过 (i, j)
return
# 进入节点 (i, j)
visited[i][j] = True
dfs(grid, i - 1, j, visited) # 上
dfs(grid, i + 1, j, visited) # 下
dfs(grid, i, j - 1, visited) # 左
dfs(grid, i, j + 1, visited) # 右
另外,还有一种方向数组的方法:
"""方向数组的方法"""
# 方向数组,分别代表上、下、左、右
dirs = [[-1,0], [1,0], [0,-1], [0,1]]
def dfs_withdirs(grid, i: int, j: int, visited) -> None:
m, n = len(grid), len(grid[0])
if i < 0 or j < 0 or i >= m or j >= n: # 超出索引边界
return
if visited[i][j]: # 已遍历过 (i, j)
return
# 进入节点 (i, j)
visited[i][j] = True
# 递归遍历上下左右的节点
for d in dirs:
next_i = i + d[0]
next_j = j + d[1]
dfs_withdirs(grid, next_i, next_j, visited)
# 离开节点 (i, j)
那么本题的关键就在于,如何遍历图标记出值为1且连通的部分。其实就是遍历二维数组(图)的每个值(节点),如果发现岛屿“1”,就使用DFS将连通的部分全部变为“0”(淹没岛屿),最后返回发现的岛屿数即可。
# 注意:python 代码由 chatGPT🤖 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
# 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
res = 0
m, n = len(grid), len(grid[0])
# 遍历 grid
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
# 每发现一个岛屿,岛屿数量加一
res += 1
# 然后使用 DFS 将岛屿淹了
self.dfs(grid, i, j)
return res
# 从 (i, j) 开始,将与之相邻的陆地都变成海水
def dfs(self, grid: List[List[str]], i: int, j: int) -> None:
m, n = len(grid), len(grid[0])
if i < 0 or j < 0 or i >= m or j >= n:
# 超出索引边界
return
if grid[i][j] == '0':
# 已经是海水了
return
# 将 (i, j) 变成海水
grid[i][j] = '0'
# 淹没上下左右的陆地
self.dfs(grid, i + 1, j)
self.dfs(grid, i, j + 1)
self.dfs(grid, i - 1, j)
self.dfs(grid, i, j - 1)
使用方向数组的方法:
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
m, n = len(grid), len(grid[0])
res = 0
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
res += 1
self.dfs_withdirs(grid, i, j)
return res
def dfs_withdirs(self, grid, i: int, j: int) -> None:
m, n = len(grid), len(grid[0])
if i < 0 or j < 0 or i >= m or j >= n: # 超出索引边界
return
if grid[i][j] == '0':
return
grid[i][j] = '0' # 淹没节点 (i, j)
dirs = [[0,1], [0,-1], [1,0], [-1,0]]
for d in dirs:
next_i = i + d[0]
next_j = j + d[1]
self.dfs_withdirs(grid, next_i, next_j)