深度优先搜索DFS

Posted JustDocu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度优先搜索DFS相关的知识,希望对你有一定的参考价值。

本文是对leetcode中部分涉及深度优先搜索的题目进行学习与探索

257. 二叉树的所有路径

难度:简单

给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

示例:

输入:

   1
 /   \
2     3
 \
  5

输出: ["1->2->5""1->3"]

解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> [str]:
        res = list()

        def dfs(node, path):
            if node:
                path += str(node.val)
                if not node.left and not node.right:
                    res.append(path)
                path += "->"
                dfs(node.left, path)
                dfs(node.right, path)

        dfs(root, "")
        return res

695. 岛屿的最大面积

难度:中等

给定一个包含了一些 01 的非空二维数组 grid

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

示例 1:

[[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]]

对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1

示例 2:

[[0,0,0,0,0,0,0,0]]

对于上面这个给定的矩阵, 返回 0

注意: 给定的矩阵grid 的长度和宽度都不超过 50。

思路:

  1. 使用深度优先遍历,对所给的矩阵进行遍历
  2. 当遇到一个坐标处是岛屿(值为1)时,则对其上下左右四个位置依次进行遍历搜索,当某个位置值也为1时,再对这个位置的四个方向也依次进行搜索,如此递归下去
  3. 上述第二步有个问题,就是缺少递归边界,这里需要直接将访问过的节点值置为0,然后设置边界条件:1. 当递归超出矩阵范围,停止;2. 当递归到的节点值为0,停止

代码:

class Solution:
    def maxAreaOfIsland(self, grid: [[int]]) -> int:
        height = len(grid)
        width = len(grid[0])

        def dfs(grid: [[int]], h: int, w: int) -> int:
            # 搜索边界条件
            if h < 0 or w < 0 or h >= height or w >= width or grid[h][w] == 0:
                return 0
            # 对已搜索的区域置0
            grid[h][w] = 0
            max_area = 1
            # 对当前方位的上下左右四个方向进行搜索
            for dx, dy in [[01], [10], [0-1], [-10]]:
                next_h, next_w = h + dx, w + dy
                max_area += dfs(grid, next_h, next_w)
            return max_area

        res = 0
        for i in range(height):
            for j in range(width):
                if grid[i][j] == 1:
                    res = max(res, dfs(grid, i, j))
        return res

547. 朋友圈

难度:中等

示例 1:

输入:
[[1,1,0],
 [1,1,0],
 [0,0,1]]
输出:2
解释:已知学生 0 和学生 1 互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回 2 。

示例 2:

输入:
[[1,1,0],
 [1,1,1],
 [0,1,1]]
输出:1
解释:已知学生 0 和学生 1 互为朋友,学生 1 和学生 2 互为朋友,所以学生 0 和学生 2 也是朋友,所以他们三个在一个朋友圈,返回 1 。

代码:

class Solution:
    def findCircleNum(self, M: [[int]]) -> int:
        num_s = len(M)
        # 记录已经访问过的节点
        visited = set()

        def dfs(i: int):
            # 设置递归边界:当自身置0时,说明这个节点已经遍历过了
            if M[i][i] != 0:
                for j in range(num_s):
                    if M[i][j] == 1:
                        # 将遍历过的节点置0
                        M[i][j] = 0
                        visited.add(j)
                        dfs(j)

        num_circle = 0
        for i in range(num_s):
            # 遍历未访问过的节点进行累加
            if i not in visited:
                dfs(i)
                num_circle += 1
        return num_circle

417. 太平洋大西洋水流问题

难度:中等

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

提示:

  1. 输出坐标的顺序不重要
  2. mn 都小于150

示例:

给定下面的 5x5 矩阵:

  太平洋 ~   ~   ~   ~   ~ 
       ~  1   2   2   3  (5) *
       ~  3   2   3  (4) (4) *
       ~  2   4  (5)  3   1  *
       ~ (6) (7)  1   4   5  *
       ~ (5)  1   1   2   4  *
          *   *   *   *   * 大西洋

返回:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中带括号的单元).

思路:

  • 这题整体思路就是找出分别流入大西洋和太平洋水流经的区域,然后取交集即可。

  • 如果全图搜索某个位置的水向下流会流经的区域以及大洋,这样复杂度会很高,而且也会造成重复搜索。

  • 因此这里可以反过来思考,流经这两个大洋的所有水流必经入海口。那么我们可以对流经入海口的水进行从低向高的深度遍历,即可获得所有流经的区域。

代码:

class Solution:
    def pacificAtlantic(self, matrix: [[int]]) -> [[int]]:
        height = len(matrix)
        if height == 0:
            return []
        width = len(matrix[0])
        if width == 0:
            return []

        # 用集合初始化流向太平洋和大西洋的水流分别经过的位置
        pacific = set()
        atlantic = set()
        
        # 深度优先搜索,从低往高处搜索
        def dfs(i, j, res) -> set():
            res.add((i, j))
            for dx, dy in [[01], [10], [0-1], [-10]]:
                idx_x = i + dx
                idx_y = j + dy
                if idx_x < 0 or idx_y < 0 or idx_x >= height or idx_y >= width:
                    continue
                # 如果隔壁的位置高于本地,那继续搜索
                if matrix[idx_x][idx_y] >= matrix[i][j] and (idx_x, idx_y) not in res:
                    dfs(idx_x, idx_y, res)

        # 遍历入海口地点进行dfs即可找到所有流经的地点
        for i in range(height):
            dfs(i, 0, pacific)
            dfs(i, width - 1, atlantic)
        for j in range(width):
            dfs(0, j, pacific)
            dfs(height - 1, j, atlantic)

        return pacific & atlantic

130. 被围绕的区域

难度:中等

给定一个二维的矩阵,包含 'X''O'字母 O)。

找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

示例:

X X X X
X O O X
X X O X
X O X X

运行你的函数后,矩阵变为:

X X X X
X X X X
X X X X
X O X X

解释:

被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

代码

class Solution:
    def solve(self, board: [[str]]) -> None:
        height = len(board)
        if height == 0:
            return
        width = len(board[0])
        if width == 0:
            return

        # 深度优先搜索标记出所有未被围绕的"O"
        def dfs(px, py):
            if px < 0 or px > height - 1 or py < 0 or py > width - 1 or board[px][py] != 'O':
                return
            board[px][py] = "#"
            for dx, dy in [[01], [0-1], [10], [-10]]:
                px_next = px + dx
                py_next = py + dy
                dfs(px_next, py_next)

        for i in range(height):
            dfs(i, 0)
            dfs(i, width-1)
        for j in range(width):
            dfs(0, j)
            dfs(height-1, j)

        for i in range(height):
            for j in range(width):
                if board[i][j] == 'O':
                    board[i][j] = 'X'
                if board[i][j] == '#':
                    board[i][j] = 'O'
        return board

参考资料

leetcode中文官网:https://leetcode-cn.com/


以上是关于深度优先搜索DFS的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法图遍历算法 ( 深度优先搜索 DFS | 深度优先搜索和广度优先搜索 | 深度优先搜索基本思想 | 深度优先搜索算法步骤 | 深度优先搜索理论示例 )

深度优先搜索 DFS(Depath First Search, DFS)

LeetCode 988:回溯和深度优先搜索(DFS)的区别

DFS(深度优先搜索)

深度优先搜索和广度优先搜索的简单对比

基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)