DFS 和 BFS 的适用场景
在实际的算法问题中,DFS 算法常用来穷举所有路径,BFS 算法常用来寻找最短路径,这是什么原因呢?
因为二叉树的递归遍历和层序遍历就是最简单的 DFS 算法和 BFS 算法,所以本文就用一道简单的二叉树例题,说明其中的道理。
为什么 BFS 常用来寻找最短路径 用可视化面板结合一道例题,你立刻就能明白了。
来看力扣第 111 题二叉树的最小深度:
二叉树的最小深度即「根节点到最近的叶子节点的距离」,所以这道题本质上就是让你求最短距离。
DFS 递归遍历和 BFS 层序遍历都可以解决这道题,先看 DFS 递归遍历的解法:
func minDepth(root *TreeNode) int {
if root == nil {
return 0
}
// 记录最小深度(根节点到最近的叶子节点的距离)
minDepthValue := math.MaxInt32
// 记录当前遍历到的节点深度
currentDepth := 0
var traverse func(*TreeNode)
traverse = func(root *TreeNode) {
if root == nil {
return
}
// 前序位置进入节点时增加当前深度
currentDepth++
// 如果当前节点是叶子节点,更新最小深度
if root.Left == nil && root.Right == nil {
minDepthValue = min(minDepthValue, currentDepth)
}
traverse(root.Left)
traverse(root.Right)
// 后序位置离开节点时减少当前深度
currentDepth--
}
// 从根节点开始 DFS 遍历
traverse(root)
return minDepthValue
}
每当遍历到一条树枝的叶子节点,就会更新最小深度,当遍历完整棵树后,就能算出整棵树的最小深度。
你能不能在不遍历完整棵树的情况下,提前结束算法?不可以,因为你必须确切的知道每条树枝的深度(根节点到叶子节点的距离),才能找到最小的那个。
下面来看 BFS 层序遍历的解法。按照 BFS 从上到下逐层遍历二叉树的特点,当遍历到第一个叶子节点时,就能得到最小深度:
func minDepth(root *TreeNode) int {
if root == nil {
return 0
}
q := []*TreeNode{root}
// root 本身就是一层,depth 初始化为 1
depth := 1
for len(q) > 0 {
sz := len(q)
// 遍历当前层的节点
for i := 0; i < sz; i++ {
cur := q[0]
q = q[1:]
// 判断是否到达叶子结点
if cur.Left == nil && cur.Right == nil {
return depth
}
// 将下一层节点加入队列
if cur.Left != nil {
q = append(q, cur.Left)
}
if cur.Right != nil {
q = append(q, cur.Right)
}
}
// 这里增加步数
depth++
}
return depth
}
当它遍历到第二行的时候,就遇到第一个叶子节点了,这个叶子节点就是距离根节点最近的叶子节点,所以此时算法就结束了。BFS 算法并没有遍历整棵树就找到了最小深度。
综上,你应该能理解为啥 BFS 算法经常用来寻找最短路径了:
由于 BFS 逐层遍历的逻辑,第一次遇到目标节点时,所经过的路径就是最短路径,算法可能并不需要遍历完所有节点就能提前结束。
DFS 遍历当然也可以用来寻找最短路径,但必须遍历完所有节点才能得到最短路径。
从时间复杂度的角度来看,两种算法在最坏情况下都会遍历所有节点,时间复杂度都是 O(N),但在一般情况下,显然 BFS 算法的实际效率会更高。所以在寻找最短路径的问题中,BFS 算法是首选。
为什么 DFS 常用来寻找所有路径
在寻找所有路径的问题中,你会发现 DFS 算法用的比较多,BFS 算法似乎用的不多。
理论上两种遍历算法都是可以的,只不过 BFS 算法寻找所有路径的代码比较复杂,DFS 算法代码比较简洁。
你想啊,就以二叉树为例,如果要用 BFS 算法来寻找所有路径(根节点到每个叶子节点都是一条路径),队列里面就不能只放节点了,而需要使用
二叉树层序遍历的第三种写法,新建一个 State 类,把当前节点以及到达当前节点的路径都存进去,这样才能正确维护每个节点的路径,最终计算出所有路径。
而使用 DFS 算法就简单了,它本就是一条树枝一条树枝从左往右遍历的,每条树枝就是一条路径,递归遍历到叶子节点的时候递归路径就是一条树枝,所以 DFS 算法天然适合寻找所有路径。
综上,DFS 算法在寻找所有路径的问题中更常用,而 BFS 算法在寻找最短路径的问题中更常用。