详说广度优先搜索BFS的用法

BFS(广度优先搜索)和DFS(深度优先搜索)总是会被一起提及。在实际应用中,我们用DFS的时候远远多于BFS,那么什么使用场景是DFS不能够做到的,只能使用BFS遍历层序遍历、最短路径

  1. BFS与DFS,我们先看看在二叉树上进行DFS遍历和BFS遍历的代码比较
    BFS遍历使用递归
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();
		if(node.left!=null){
			queue.add(node.left);
		}
		if(node.right!=null){
			queue.add(node.right);
		}
	}
}

比较两段代码的话,可以看出DFS的代码更整洁,空间复杂度更低,所以对于树或者图简单的遍历我们一般会选择使用DFS。
2. 虽然DFS和BFS都是将二叉树的节点遍历了一遍,但他们遍历节点的顺序不同:
请添加图片描述
BFS这个遍历顺序是能够用来解层序遍历、最短路径问题的根本原因,下面结合几道算法题来讲讲具体怎么实现的。

BFS应用1——层序遍历
请添加图片描述
解题思路:

  • 什么是层序遍历?结合二叉树来看,层序遍历就是把二叉树分层,然后每一层从左到右遍历,这个遍历顺序和BFS遍历顺序一致。
  • 但是层序遍历要求我们区分每一层,也就是返回一个二维数组,而 BFS 的遍历结果是一个一维数组,无法区分每一层。
  • 因此我们需要稍微修改一下代码,在每一层遍历之前,先记录队列中节点数量n(也就是这一层的结点数),然后一口气处理完这一层的n个节点。
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
    	// 最终的返回队列
        List<List<Integer>> list = new ArrayList<>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root!=null){
            queue.add(root);
        }
        while(!queue.isEmpty()){
      		// 存储不同层的节点
            List<Integer> list1 = new ArrayList<>();
            // 在每一层遍历开始前,先记录队列中的结点数量 n
            int n = queue.size();
            // 一口气处理完这一层的 n 个结点
            for(int i=0;i<n;i++){
                TreeNode node = queue.poll();
                // 把该层节点的值加入到队列中
                list1.add(node.val);
                if(node.left!=null){
                    queue.add(node.left);
                }
                if(node.right!=null){
                    queue.add(node.right);
                }
            }
            // 每一层的遍历结果放入队列
            list.add(list1);
        }
        return list;
    }
}

BFS应用2——最短路径
请添加图片描述
解题思路:

  • 在二叉树中,BFS可以实现一层一层的遍历,在图中同样如此。从源点除法,BFS首先遍历到第一层节点,到源点的距离为1,然后遍历到第二层节点,到源点距离为2…可以看到,用BFS的话,距离源点更近的点会被先遍历到,这样就能找到某个点的最短路径了。
  • 仿照上面的二叉树层序遍历代码,类似地可以写出网格层序遍历:将遍历过的格子标记为 2,避免重复遍历!!
  • 由于一个格子有四个相邻的格子,判断四遍格子坐标的合法性可以优化,使用一个数组存储相邻格子的四个方向(上下左右)
  • 假设网格中只有一个陆地格子,我们可以从这个陆地格子出发做层序遍历,直到所有格子都遍历完。最终遍历了几层,海洋格子的最远距离就是几。
  • BFS 完全可以以多个格子同时作为起点。我们可以把所有的陆地格子同时放入初始队列,然后开始层序遍历。
//网格结构的层序遍历
//从格子(i,j)开始遍历

public int maxDistance(int[][] grid) {
	// 使用数组存储相邻格子的四个方向(上下左右)
    int[][]moves = new int [][]{{1,0},{-1,0},{0,-1},{0,1}};
    Queue<int[]> queue = new ArrayDeque<>();
    // 把所有的陆地格子加入队列,成为多源BFS
    for(int i=0;i<grid.length;i++){
        for(int j=0;j<grid[0].length;j++){
            if(grid[i][j]==1)
                queue.add(new int[]{i,j});
        }
    }
    //记录当前遍历的层数
    int distance =-1;
    
    while(!queue.isEmpty()){
        distance++;
        int n = queue.size();
        for(int i=0;i<n;i++){
            int [] node = queue.poll();
            // 横坐标
           int r = node[0];
           // 纵坐标
           int c = node[1];
           for(int [] move:moves){
               int r2 =r+move[0];
               int c2 =c+move[1];
               if(r2>=0 && r2<grid.length && c2>=0 && c2<grid[0].length && grid[r2][c2]==0){
               	   // 将遍历过的格子标记为 2
                   grid[r2][c2] =2;
                   // 将遍历过的格子加入到队列中,继续向四个方向遍历寻找海洋格子
                   queue.add(new int[]{r2,c2});
               }
           }
        }
        
    }
}

总结一下广度优先遍历/搜索应用的步骤:

  1. 第一步:设置队列,添加初始队列节点;
  2. 第二步:判断队列是否为空;
  3. 第三步:迭代操作,弹出队列元素,进行逻辑处理,当前队列元素的下级元素,入队;
  4. 第四步:再次执行步骤三

如果这篇文章对你有帮助的话,就留下一个赞👍吧!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值