广度优先算法(包含6道力扣题,写完直接入门)

了解广度优先算法前,应该知道一些东西:

图的建立方法:

在这里插入图片描述

1.邻接矩阵

变为邻接矩阵(通则为true,不通则为false):
在这里插入图片描述

2.邻接表(该节点相通的则放进一张表里)

在这里插入图片描述

模板:

 // bfs,最常用于最小路径问题
        Queue<> queue = new LinkedList<>();
        // 这里是假设元素不会重复,所以用Set来判断一个元素是否已经读取过
        Set<> visited = new Set<>();
        queue.offer(起点);
        visited.add(起点);
        int step = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size > 0) {
                Node node = queue.poll();
                // 判断是否是终点
                if(终点满足的条件){
                    return step;
                }
                // 不是终点,将这个点的邻域点放入队列
                for(Node x : node所连接的节点){
                    if(x not in visited ){
                        queue.offer(x);
                        visited.add(x);  
                    }
                }
            }
            // step++放在这个位置是因为bfs是一层层往外推进,走完一层之后再++
            step++;

概念:

广度优先搜索(也称宽度优先搜索)是连通图的一种遍历算法,也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和广度优先搜索类似的思想。属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。基本过程,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点,也就是一层一层的遍历。如果所有节点均被访问,则算法中止。一般用队列数据结构来辅助实现BFS算法。

解决问题:

第一类问题:从节点A出发,有前往节点B的路径吗?
第二类问题:从节点A出发,前往节点B的哪条路径最短?
层序遍历、最短路径、求二叉树的最大高度、由点到面遍历图、拓扑排序。

解题步骤:

1.利用队列实现
2.从源节点开始依次按照宽度进队列,然后弹出
3.每弹出一个节点,就把该节点所有没有进过队列的邻接点放入队列
4.直到队列为空

相关力扣题目:

429 N 叉树的层序遍历
题目:
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。

思路:
简单的层序遍历问题,只需要将普通的二叉树变为n叉树,即修改插入队列的代码,即可得出答案。

代码:
(补充:加强for循环):

public static void main(String[] args){
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ){
         System.out.print( x );
         System.out.print(",");
      }
      System.out.print("\n");
      String [] names ={"James", "Larry", "Tom", "Lacy"};
      for( String name : names ) //for(声明语句 : 表达式){
         System.out.print( name );
         System.out.print(",");
      }
}
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

//时间:91.59  内存:71.28    难度:中等   题目:N 叉树的层序遍历    2022.8.18
public class li_429 {
    class Node {
        public int val;
        public List<Node> children;

        public Node() {
        }

        public Node(int _val) {
            val = _val;
        }

        public Node(int _val, List<Node> _children) {
            val = _val;
            children = _children;
        }
    };
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> fin = new ArrayList<>();
        Queue<Node> now=new LinkedList<>();
        if(root==null){
            return fin;
        }
        now.offer(root);
        while(!now.isEmpty()){
            int n=now.size();
            List<Integer> k=new ArrayList<>();
            while(n!=0){
                Node m=now.poll();
                k.add(m.val);
                n-=1;
                int l=0;
                while(l!=m.children.size()){
                    Node chil=m.children.get(l);
                    l++;
                    now.offer(chil);
                }
            }
            fin.add(k);
        }
        return fin;
    }



    //递归思想
    public List<List<Integer>> levelOrder_2(Node root) {
        List<List<Integer>> fin = new ArrayList<>();
        Queue<Node> now=new LinkedList<>();
        if(root==null){
            return fin;
        }
        now.offer(root);
        bfs(fin,now);
        return fin;
    }
    public void bfs(List<List<Integer>> fin,Queue<Node> now){
        if(now.isEmpty()){
            return ;
        }else {
            int n=now.size();
            List<Integer> k=new ArrayList<>();
            while(n!=0){
                Node m=now.poll();
                k.add(m.val);
                n-=1;
                int l=0;
                while(l!=m.children.size()){
                    now.offer(m.children.get(l));
                    l++;
                }
            }
            fin.add(k);
            bfs(fin,now);
        }
    }
}

面试题04.01 节点间通路
题目:
节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。

思路:
基础广度优先算法题,即判断路线是否可行,个人认为题目难点在于如何建图,然后通过图进行层序遍历(BFS),直到找到target为止。

代码:

package hanjia_lianxi;

import java.util.*;

//时间:53.70  内存:54.55    难度:中等   题目:节点间通路    2022.8.18
public class li_04_01 {
    public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
        //邻接表
        List[] list=new ArrayList[n];
        Queue<Integer> queue=new LinkedList<>();
        for(int k=0;k<graph.length;k++){
            if(list[graph[k][0]]==null){
                list[graph[k][0]]=new ArrayList<Integer>();
            }
            if(list[graph[k][0]].contains(graph[k][1])){
                continue;
            }
            list[graph[k][0]].add(graph[k][1]);
        }
        queue.add(start);

        while(!queue.isEmpty()){
            if(queue.contains(target)){
                return true;
            }
            int k=queue.size();
            while(k!=0){
                int now=queue.poll();
                k-=1;
                if(list[now]==null){
                    continue;
                }
                for(int m=0;m<list[now].size();m++){
                    if(list[now].get(m).equals(target)){
                        return true;
                    }else{
                        queue.add((Integer) list[now].get(m));
                    }
                }
            }
        }
        return false;
    }
}

1036 跳跃游戏III
题目:
这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]。
请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。
注意,不管是什么情况下,你都无法跳到数组之外。

例子:
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3

思路:
首先给定一个起点,这就相当于一颗二叉树的头结点,然后判定相加减的条件下是否还在数组范围中,如果在则充当该节点的孩子节点,然后进行类似层序遍历,直到找到0所在的节点。

代码:

package hanjia_lianxi;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

//时间:15.33  内存:94.36    难度:中等   题目:跳跃游戏 III 2022.8.18   
public class li_1306 {
    public boolean canReach(int[] arr, int start) {
        Boolean[] road=new Boolean[arr.length];
        Arrays.fill(road,false);
        road[start]=true;
        Queue<Integer> queue=new LinkedList<>();
        queue.offer(start);
        while(!queue.isEmpty()){
            int n=queue.size();
            while(n!=0){
                if(queue.contains(0)){
                    return true;
                }
                int now=queue.poll();
                n-=1;
                if(now+arr[now]<arr.length&&!road[now+arr[now]]){
                    road[now+arr[now]]=true;
                    queue.offer(now+arr[now]);
                }
                if(now-arr[now]>=0&&!road[now-arr[now]]){
                    road[now-arr[now]]=true;
                    queue.offer(now-arr[now]);
                }
            }
        }
        return false;
    }


}

863二叉树中所有距离为 K 的结点
题目:
给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 k 。返回到目标结点 target 距离为 k 的所有结点的值的列表。 答案可以以 任何顺序 返回。
例子:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, k = 2
输出:[7,4,1]
解释:所求结点为与目标结点(值为 5)距离为 2 的结点,值分别为 7,	     4,以及 1

思路:
如果按照BFS思想,这是一个变相的寻找通路问题,如果按照BFS的写法,需要先将二叉树化成一个图,图用邻接表表示,然后k值就是其他节点距离该节点的距离,通过访问目标节点的邻接点,再访问邻接点的邻接点,知道找到距离为k的节点为止(或找不到,找的方法是k–,直到k为0则找到),注意的是,二叉树转变为图的时候,是双向图,所以需要给一个boolean表来判定该节点是否走过,以免重复死循环。

代码:

package hanjia_lianxi;

import java.util.*;


//时间:5.22   内存:67.70    难度:中等   题目:二叉树中所有距离为 K 的结点  2022.8.19
public class li_863 {
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode(int x) {
            val = x;
        }
    }

    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        List<Integer>[] graph=new ArrayList[500];
        Queue<TreeNode> queue=new LinkedList();
        queue.offer(root);
        while(!queue.isEmpty()){
            int num=queue.size();
            while(num!=0){
                num-=1;
                TreeNode now=queue.poll();
                if(graph[now.val]==null){
                    graph[now.val]=new ArrayList<>();
                }
                if(now.left!=null){
                    queue.offer(now.left);
                    if(graph[now.left.val]==null){
                        graph[now.left.val]=new ArrayList<>();
                        graph[now.val].add(now.left.val);
                        graph[now.left.val].add(now.val);
                    }
                }
                if(now.right!=null){
                    queue.offer(now.right);
                    if(graph[now.right.val]==null){
                        graph[now.right.val]=new ArrayList<>();
                        graph[now.val].add(now.right.val);
                        graph[now.right.val].add(now.val);
                    }
                }
            }
        }
        List<Integer> fin=new ArrayList<>();
        Queue<Integer> queue2=new LinkedList();
        queue2.add(target.val);
        Boolean[] booleans=new Boolean[500];
        Arrays.fill(booleans,false);
        while(!queue2.isEmpty()){
            if(k==0){
                break;
            }
            int num=queue2.size();
            while(num!=0){
                num-=1;
                int now=queue2.poll();
                booleans[now]=true;
                if(graph[now]==null){
                    continue;
                }
                for(int m=0;m<graph[now].size();m++){
                    if(booleans[graph[now].get(m)]==true){
                        continue;
                    }
                    queue2.offer(graph[now].get(m));
                    booleans[graph[now].get(m)]=true;
                }
            }
            k-=1;
        }
        if(k>0){
            return fin;
        }
        while(!queue2.isEmpty()){
            fin.add(queue2.poll());
        }
        return fin;
    }
}

54201矩阵
题目:
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。

例子:
         
输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]

思路:
可以将矩阵想想为一张全联通图,设定一个boolean表,用于判定该点是否被访问过,然后将为0的点压入到队列中,然后1点则将boolean表对应值置true,最后常规的BFS算法去访问队列中节点的相连点,如果是true,则将该点的值赋为出队列点+1(距离零点的距离加1),并且加入到队列中,boolean设为false,反复该操作直到队列为空,返回矩阵。

代码:

package hanjia_lianxi;

import java.util.LinkedList;
import java.util.Queue;

//难度:中等    题目:01矩阵
public class li_542 {
   //时间:18.78 内存:97.92   2022.8.22  BFS
   public int[][] updateMatrix(int[][] mat) {
      Queue<int[]> queue=new LinkedList<>();
      boolean[][] road=new boolean[mat.length][mat[0].length];
      for(int n=0;n< mat.length;n++){
         for(int m=0;m<mat[0].length;m++){
            if(mat[n][m]==0){
               int[] now={n,m};
               queue.add(now);
            }else{
               mat[n][m]=-1;
               road[n][m]=true;
            }
         }
      }
      while(!queue.isEmpty()){
         int[] now=queue.poll();
         int nown=mat[now[0]][now[1]];
         int[] testx={0,0,1,-1};
         int[] testy={1,-1,0,0};
         for(int n=0;n<4;n++){
            int x=now[0]+testx[n];
            int y=now[1]+testy[n];
            if(x>=0&&x<mat.length&&y>=0&&y<mat[0].length&&road[x][y]){
               mat[x][y]=nown+1;
               int[] k={x,y};
               queue.offer(k);
               road[x][y]=false;
            }
         }
      }
      return mat;
   }
}

1765地图中的最高点
题目:
给你一个大小为 m x n 的整数矩阵 isWater ,它代表了一个由 陆地 和 水域 单元格组成的地图。
如果 isWater[i][j] == 0 ,格子 (i, j) 是一个 陆地 格子。
如果 isWater[i][j] == 1 ,格子 (i, j) 是一个 水域 格子。
你需要按照如下规则给每个单元格安排高度:
每个格子的高度都必须是非负的。
如果一个格子是 水域 ,那么它的高度必须为 0 。
任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格子。(也就是说它们有一条公共边)找到一种安排高度的方案,使得矩阵中的最高高度值 最大 。
请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回 任意一个 。

思路:
01矩阵变相题目,思路相同
代码:

	package hanjia_lianxi;

import java.util.LinkedList;
import java.util.Queue;
//时间:15.31  内存:63.16    难度:中等   题目:地图中的最高点  2022.8.22
public class li_1765 {
    public int[][] highestPeak(int[][] isWater) {
        Queue<int[]> queue = new LinkedList<>();
        boolean[][] graph = new boolean[isWater.length][isWater[0].length];
        for (int n = 0; n < isWater.length; n++) {
            for (int m = 0; m < isWater[0].length; m++) {
                if (isWater[n][m] == 1) {
                    int[] now = {n, m};
                    isWater[n][m] = 0;
                    queue.offer(now);
                } else {
                    graph[n][m] = true;
                }
            }
        }
        while (!queue.isEmpty()) {
            int[] now = queue.poll();
            int[] testx={0,0,1,-1};
            int[] testy={1,-1,0,0};
            for(int n=0;n<4;n++){
                int x=now[0]+testx[n];
                int y=now[1]+testy[n];
                if(x>=0&&x<isWater.length&&y>=0&&y<isWater[0].length&&graph[x][y]){
                    graph[x][y]=false;
                    int[] k={x,y};
                    queue.offer(k);
                    isWater[x][y]=isWater[now[0]][now[1]]+1;
                }
            }
        }
        return isWater;
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值