BFS 的使用场景总结:层序遍历、最短路径等问题

DFS 与 BFS 回顾

让我们先看看在二叉树上进行 DFS 遍历和 BFS 遍历的代码比较。

DFS 遍历使用递归:

void dfs(TreeNode root) {
    
    
    if (root == null) {
    
    
        return;
    }
    dfs(root.left);
    dfs(root.right);
}

BFS 遍历使用队列数据结构:

void bfs(TreeNode root) {
    
    
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
    
    
        TreeNode node = queue.poll(); // Java 的 pop 写作 poll()
        if (node.left != null) {
    
    
            queue.add(node.left);
        }
        if (node.right != null) {
    
    
            queue.add(node.right);
        }
    }
}

只是比较两段代码的话,最直观的感受就是:DFS 遍历的代码比 BFS 简洁太多了这是因为递归的方式隐含地使用了系统的 栈,我们不需要自己维护一个数据结构。如果只是简单地将二叉树遍历一遍,那么 DFS 显然是更方便的选择。

虽然 DFS 与 BFS 都是将二叉树的所有结点遍历了一遍,但它们遍历结点的顺序不同。

这个遍历顺序也是 BFS 能够用来解「层序遍历」、「最短路径」问题的根本原因。下面,我们结合几道例题来讲讲 BFS 是如何求解层序遍历和最短路径问题的。

BFS 的应用一:层序遍历

BFS 的层序遍历应用就是本题了:

LeetCode 102. Binary Tree Level Order Traversal 二叉树的层序遍历(Medium)

给定一个二叉树,返回其按层序遍历得到的节点值。 层序遍历即逐层地、从左到右访问所有结点。

什么是层序遍历呢?简单来说,层序遍历就是把二叉树分层,然后每一层从左到右遍历:
在这里插入图片描述
乍一看来,这个遍历顺序和 BFS 是一样的,我们可以直接用 BFS 得出层序遍历结果。然而,层序遍历要求的输入结果和 BFS 是不同的。层序遍历要求我们区分每一层,也就是返回一个二维数组。而 BFS 的遍历结果是一个一维数组,无法区分每一层。

在这里插入图片描述
那么,怎么给 BFS 遍历的结果分层呢?我们首先来观察一下 BFS 遍历的过程中,结点进队列和出队列的过程:
在这里插入图片描述
截取 BFS 遍历过程中的某个时刻:
在这里插入图片描述
可以看到,此时队列中的结点是 3、4、5,分别来自第 1 层和第 2 层。这个时候,第 1 层的结点还没出完,第 2 层的结点就进来了,而且两层的结点在队列中紧挨在一起,我们无法区分队列中的结点来自哪一层。

因此,我们需要稍微修改一下代码,在每一层遍历开始前,先记录队列中的结点数量 n(也就是这一层的结点数量),然后一口气处理完这一层的 n 个结点。

vector<vector<int>> levelOrder(TreeNode* root) {
    
    
	vector<vector<int>> V;
	queue<TreeNode*> Q;
	if(root==NULL){
    
    
		return {
    
    };
	}
	Q.push(root);
	while(!Q.empty()){
    
    
		int n  = Q.size();//计算当前层元素的个数 
		vector<int> v;
		for(int i = 0;i <n;i++){
    
    //将当前层元素弹出放到v中. 
			TreeNode* node = Q.front();
			Q.pop();
		
			v.push_back(node->val);
			if(node->left){
    
    
				Q.push(node->left);
			}
			if(node->right){
    
    
				Q.push(node->right);
			}			
		}
		V.push_back(v); 
	}
	return V;
}

这样,我们就将 BFS 遍历改造成了层序遍历。在遍历的过程中,结点进队列和出队列的过程为:
在这里插入图片描述
可以看到,在 while 循环的每一轮中,都是将当前层的所有结点出队列,再将下一层的所有结点入队列,这样就实现了层序遍历。

BFS 的应用二:最短路径

在一棵树中,一个结点到另一个结点的路径是唯一的,但在图中,结点之间可能有多条路径,其中哪条路最近呢?这一类问题称为最短路径问题。最短路径问题也是 BFS 的典型应用,而且其方法与层序遍历关系密切。

在二叉树中,BFS 可以实现一层一层的遍历。在图中同样如此。从源点出发,BFS 首先遍历到第一层结点,到源点的距离为 1,然后遍历到第二层结点,到源点的距离为 2…… 可以看到,用 BFS 的话,距离源点更近的点会先被遍历到,这样就能找到到某个点的最短路径了。
在这里插入图片描述
稍后刷到该知识点再进行补充。。

本文参考自LeetCode大神的BFS 的使用场景总结:层序遍历、最短路径问题

Guess you like

Origin blog.csdn.net/LXYDSF/article/details/121197729