深度优先搜索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. 岛屿的最大面积
难度:中等
给定一个包含了一些 0
和 1
的非空二维数组 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)时,则对其上下左右四个位置依次进行遍历搜索,当某个位置值也为1时,再对这个位置的四个方向也依次进行搜索,如此递归下去 -
上述第二步有个问题,就是缺少递归边界,这里需要直接将访问过的节点值置为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 [[0, 1], [1, 0], [0, -1], [-1, 0]]:
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
的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。
规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。
请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。
提示:
-
输出坐标的顺序不重要 -
m 和 n 都小于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 [[0, 1], [1, 0], [0, -1], [-1, 0]]:
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 [[0, 1], [0, -1], [1, 0], [-1, 0]]:
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)