「代码随想录」图论之图的DFS遍历

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(图,选择的节点)  # 递归
	    回溯,撤销处理结果

参考卡哥给出的深搜三部曲:

  1. 首先需要确定递归函数&参数
def dfs(self, graph, root: int):

此处的graph为题目给出的图,root为当前搜索的根节点

  1. 确认终止条件:
    当目前遍历的节点 为 最后一个节点的时候,就找到了一条,从 出发点到终止点的路径。即root==len(graph)-1,来到最后一个节点的时候,path的结果可以被添加到result中来。并返回。

  2. 处理目前搜索节点出发的路径,遍历每一个节点然后回溯

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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值