DFS还是BFS,深度优于广度?三道LeetCode题让你悟透二者优劣!
2024-02-05 01:28:04
深度优先搜索(DFS)和广度优先搜索(BFS):探索多维数据结构的利器
在算法面试和刷题中,深度优先搜索(DFS)和广度优先搜索(BFS)是两个绕不开的概念。它们都是遍历图和树等数据结构的常用技术,但由于遍历方式不同,在不同的场景下具有各自的优缺点。本文将深入浅出地探讨 DFS 和 BFS 的原理、优劣势以及适用场景,帮助你掌握这些算法并在算法问题中游刃有余。
一、DFS:沿着深度探索数据结构
深度优先搜索(DFS)是一种沿着深度遍历数据结构的算法。它从根节点开始,逐层深入探索节点的子树,直到遇到叶子节点。然后,它回溯到上一个未完全探索的节点,继续探索其子树。DFS 的递归性质使其特别适合解决查找最长路径、环路检测和拓扑排序等问题。
二、BFS:沿着宽度探索数据结构
广度优先搜索(BFS)是一种沿着宽度遍历数据结构的算法。它从根节点开始,逐层探索节点的所有子节点,然后再继续探索下一层节点。这种方式可以保证所有节点都被访问到,然后再深入探索其中任何一个节点。BFS 适用于查找最短路径、图的连通性以及检测环路等问题。
三、DFS 和 BFS 的优劣势
DFS 和 BFS 虽然都是遍历数据结构的有效算法,但它们在时间复杂度、空间复杂度和适用场景上存在一些差异:
时间复杂度:
- DFS 的时间复杂度通常比 BFS 高,因为它需要多次访问同一个节点。
- BFS 的时间复杂度通常比 DFS 低,因为它只访问每个节点一次。
空间复杂度:
- BFS 的空间复杂度通常比 DFS 高,因为它需要存储所有未访问的节点。
- DFS 的空间复杂度通常比 BFS 低,因为它只存储当前路径上的节点。
适用场景:
- DFS 适用于需要深度探索数据结构的场景,如查找最长路径或环路。
- BFS 适用于需要广度探索数据结构的场景,如求最短路径或图的连通性。
四、LeetCode 题解示例
为了加深对 DFS 和 BFS 的理解,我们来看几个经典的 LeetCode 题目:
题目 1:路径总和
给定一个二叉树和一个目标和,求出所有从根节点到叶子节点的路径,使得这些路径上的节点值之和等于目标和。
DFS 解法:
def pathSum(root, targetSum):
if not root:
return []
paths = []
stack = [(root, [root.val], root.val)]
while stack:
node, path, sum = stack.pop()
if not node.left and not node.right and sum == targetSum:
paths.append(path)
if node.left:
stack.append((node.left, path + [node.left.val], sum + node.left.val))
if node.right:
stack.append((node.right, path + [node.right.val], sum + node.right.val))
return paths
BFS 解法:
def pathSum(root, targetSum):
if not root:
return []
queue = [(root, [root.val], root.val)]
paths = []
while queue:
node, path, sum = queue.pop(0)
if not node.left and not node.right and sum == targetSum:
paths.append(path)
if node.left:
queue.append((node.left, path + [node.left.val], sum + node.left.val))
if node.right:
queue.append((node.right, path + [node.right.val], sum + node.right.val))
return paths
题目 2:岛屿数量
给定一个由 0 和 1 组成的二维网格,其中 0 表示水域,1 表示陆地。求出网格中岛屿的个数。
DFS 解法:
def numIslands(grid):
if not grid:
return 0
m, n = len(grid), len(grid[0])
count = 0
visited = set()
def dfs(x, y):
if x < 0 or x >= m or y < 0 or y >= n or (x, y) in visited or grid[x][y] == '0':
return
visited.add((x, y))
dfs(x + 1, y) # 上
dfs(x - 1, y) # 下
dfs(x, y + 1) # 右
dfs(x, y - 1) # 左
for i in range(m):
for j in range(n):
if (i, j) not in visited and grid[i][j] == '1':
count += 1
dfs(i, j)
return count
BFS 解法:
def numIslands(grid):
if not grid:
return 0
m, n = len(grid), len(grid[0])
count = 0
visited = set()
def bfs(x, y):
queue = [(x, y)]
while queue:
x, y = queue.pop(0)
if x < 0 or x >= m or y < 0 or y >= n or (x, y) in visited or grid[x][y] == '0':
continue
visited.add((x, y))
queue.extend([(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)])
for i in range(m):
for j in range(n):
if (i, j) not in visited and grid[i][j] == '1':
count += 1
bfs(i, j)
return count
五、常见问题解答
1. 什么是 DFS 和 BFS 的时间复杂度和空间复杂度?
- DFS 的时间复杂度通常较高,为 O(V + E),其中 V 是顶点数,E 是边数。空间复杂度也较高,为 O(V)。
- BFS 的时间复杂度通常较低,为 O(V + E),空间复杂度也较低,为 O(V)。
2. 什么情况下使用 DFS 比 BFS 更好?
- 当需要深度探索数据结构时,如查找最长路径或环路时,DFS 更合适。
- 当数据结构中有许多重叠路径时,DFS 可以避免重复遍历。
3. 什么情况下使用 BFS 比 DFS 更好?
- 当需要广度探索数据结构时,如求最短路径或图的连通性时,BFS 更合适。
- 当数据结构中有许多分支时,BFS 可以保证所有节点都被访问。
4. DFS 和 BFS 的主要区别是什么?
- DFS 沿着深度遍历,BFS 沿着宽度遍历。
- DFS 的空间复杂度通常比 BFS 高。
- DFS 更适合深度探索,BFS 更适合广度探索。
5. 如何在算法面试中有效使用 DFS 和 BFS?
- 理解 DFS 和 BFS 的原理、优劣势和适用场景。
- 熟练掌握 DFS 和 BFS 的代码实现。
- 在算法问题中能够识别适合使用 DFS 或 BFS 的场景。
- 能够分析 DFS 和 BFS 的时间复杂度和空间复杂度。