了解广度优先算法前,应该知道一些东西:
图的建立方法:
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;
}
}