BFS的学习

1、队列的学习

队列是特殊的线性表,它只允许在前端进行删除操作,后端进行插入操作
然后LinkedList实现Queue接口,所以队列的话,我们可以使用LinkedList
//构造队列
	LinkedList<Integer> queue = new LinkedList<Integer>();
//入队操作
	addLast();
//出队操作
	
//删除特定元素
queue.subList(2,4).clear();	//删除[2,4)的元素

//判断队列是否为空
while(!queue.empty()){
    
}
//统计队列元素的个数
//访问队首元素
	poll();	//返回第一个元素,并且删除它
	removeFirst();//返回第一个元素并且删除他,但是为空会抛出异常,所以使用其要判空



//注意:在调用访问队首元素的时候,还有出队的时候,
//必须要先判断队列是否为空()

2、广度优先搜索(BFS模板)

bfs需要借助队列来实现

void bfs(){
    将起始点放入队列中
    标记起点访问
    while(如果队列不为空){
    	访问队首元素
        删除队首元素
        for(x所有相邻的点){
        	if(该点未被访问过且合法){
            	将该点加入队列末尾
            }
        }
    }
     队列为空,广搜结束
}

适用场景:迷宫最短路问题

例题1、岛屿的数量

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
11110
11010
11000
00000

输出: 1
示例 2:

输入:
11000
11000
00100
00011

输出: 3

代码:
package leetcode;

import java.util.LinkedList;

public class Main {
    static int[] dx={-1,0,1,0};
    static int[] dy={0,1,0,-1};
    static  boolean[][] vis;
    private static int cols;
    private static int rows;
    private static int res;
    public static void bfs(int i ,int j,char[][] grid){
        LinkedList<Integer> queue = new LinkedList<Integer>();
        vis[i][j] = true;
        queue.addLast(i*cols+j);
        while (!queue.isEmpty()){
            int cur = queue.removeFirst();
            int oldx = cur/cols;
            int oldy = cur%cols;
            for(int k = 0;k < 4 ;k ++){
                int tempx = oldx + dx[k];
                int tempy = oldy+ dy[k];
                if(isArea(tempx,tempy) && !vis[tempx][tempy] && grid[tempx][tempy]=='1'){
                    queue.addLast(tempx*cols+tempy);
                    vis[tempx][tempy] = true;
                }
            }
        }
    }
    public static boolean isArea(int x,int y){
        if(x>=0 && x<rows && y>=0 &&y<cols){
            return true;
        }
        return false;
    }


    public static int numIslands(char[][] grid) {
        if(grid.length == 0){
            return 0;
        }
        res = 0;
        rows = grid.length;
        cols = grid[0].length;
        vis = new boolean[rows][cols];
        for(int i =0;i<rows;i++){
            for (int j =0;j<cols;j++){
                if(!vis[i][j] && grid[i][j]=='1' ){
                    res++;
                    bfs(i,j,grid);


                }
            }
        }
        return res;
    }
    public static void main(String[] args) {
        char[][] grid = {
                {'1','1','1','1','0'},
                {'1','1','0','1','0'},
                {'1','1','0','0','0'},
                {'0','0','0','0','0'}
        };
        System.out.println(numIslands(grid));

        char[][] grid1 = {
                {'1', '1', '0', '0', '0'},
                {'1', '1', '0', '0', '0'},
                {'0', '0', '1', '0', '0'},
                {'0', '0', '0', '1', '1'}};
        System.out.println(numIslands(grid1));




    }
}

例题2 迷宫

【问题描述】
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可
以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这
个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,
一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,
其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中D<L<R<U。

样例
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

题解:

1、首先用bfs将最短距离求出来

2、然后将可能的情况还原出来 dirt[[x]][][y] =dirt[][]的四个方向加1

关于为什么要从后面开始,因为我从后面开始查找的话,找完最小距离之后

我们可以按照(0,0)到(n-1,m-1)的顺序去将路径标出来

代码:
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000


import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Scanner;

public class Main{
    static class Node{
        int x;
        int y;
    }
    static int m;
    static int n;
    static int[][] dist = new int[30][50];//记录其最短的距离
    static char dir[]= {'D','L','R','U'};
    static int dx[]={1,0,0,-1};     //四个方向
    static int dy[]={0,-1,1,0};
    static char[][] map ;
    static Queue<Node> q = new ArrayDeque<>();
    public static void bfs(){
        dist[n-1][m-1] =0;
        Node t = new Node();
        t.x = n -1;
        t.y = m -1;
        q.offer(t);     //入队

        while(!q.isEmpty()){
            //先删除这个,然后接着遍历其他的节点
            t = q.poll();   //出队
            for(int i =0;i<4;i++){
                int xx = t.x+dx[i];
                int yy = t.y +dy[i];
                //然后开始判断
                //1、不能越界,没有访问过,而且还是当前位置可以通过的

                if(xx>=0&&yy>=0&& xx<n&&yy<m && dist[xx][yy] == -1 && map[xx][yy]=='0'){
                    dist[xx][yy] = dist[t.x][t.y] +1;
                    Node s = new Node();
                    s.x = xx;
                    s.y = yy;
                    q.offer(s);
                }
            }
        }

    }
    //按照DLRU的顺序来
    public static void main(String[] args) {
         n =30;
         m =50;
        Scanner cin = new Scanner(System.in);
         for(int i =0;i<n;i++){
             for(int j =0;j<m;j++){
                 dist[i][j] = -1;
             }
         }
         map = new char[n][m];
        for(int i=0;i<n;i++) {
            String str=cin.next();
            for(int j=0;j<m;j++) {
                map[i][j]=str.charAt(j);
            }
        }
         bfs();
        int x =0,y = 0;
        String res ="";
        System.out.println(dist[0][0]);
        while(x!=n-1||y!=m-1){

            for(int i =0;i<4;i++){
                int xx = x+dx[i];
                int yy = y+dy[i];
                if(xx>=0&&xx<n&&yy>=0&&yy<m&& map[xx][yy]=='0'){
                    if(dist[xx][yy] == dist[x][y]-1){
                        x = xx;
                        y = yy;
                        res +=dir[i];
                    }
                }
            }
        }
        System.out.println(res);
    }
}

例题3学霸的迷宫

问题描述

  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。

输入格式

  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。

输出格式

  第一行一个数为需要的最少步数K。
  第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。

样例输入

Input Sample 1:
3 3
001
100
110

Input Sample 2:
3 3
000
000
000

样例输出

Output Sample 1:
4
RDRD

Output Sample 2:
4
DDRR

题解:

太坑了,我以为BFS是最简的问题,然后就直接设置成静态变量然后来进行计算,这样在面对第二个样例额的时候,是无法跳过的。BFS最适合的应该是岛屿问题(会把所有为0的都给遍历掉),最后的最后因为他第一个出来的一定是最优秀的情况。

这是典型的最短路径(BFS)+存储路径

因为这个是按照字典序来排列的,所以是DLRU

也就是dx[]={1,0,0,-1},dy[]={0,-1,1,0};

代码:

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

public class Main {
    static int n,m;
    static char[][] arr;
    static boolean[][] vis;
    static int res;

    static class Node{
        int x;
        int y;
        int step;
        String str;
        public Node(){};
        public Node(int x,int y,int step,String str){
            this.x =x;
            this.y=y;
            this.step = step;
            this.str = str;
        }
    }
    //DLRU 下左右上
    static int[] dy = {0,-1,1,0};
    static int[] dx ={1,0,0,-1};
    static String string ="DLRU";
    static  StringBuffer buffer;
    static void bfs(){
        Queue<Node> queue = new LinkedList<>();

        queue.offer(new Node(0,0,0,""));
        while (!queue.isEmpty()){
            Node start = queue.poll();
            if(start.x==n-1 && start.y==m-1){
//                System.out.println(buffer.toString());
                System.out.println(start.str);
                System.out.println(start.step);
                return;
            }
            for(int i =0;i<4;i++){
                int xx = start.x +dx[i];
                int yy = start.y + dy[i];
                if(xx>=0 && yy>=0 && xx<n && yy <m && arr[xx][yy]=='0' && !vis[xx][yy]){
                    vis[xx][yy] = true;
                    int step = start.step;
//                    String str = start.str+string.charAt(i);
                    queue.offer(new Node(xx,yy,step+1,start.str+string.charAt(i)));
                    buffer.append(string.charAt(i));
                }
            }

        }
    }
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);


        //为了初始化的输入
        n = cin.nextInt();
        m = cin.nextInt();
        arr = new char[n][m];
        vis = new boolean[n][m];
        for(int i =0;i < n;i++){
            String str = cin.next();
            for(int j=0;j < m;j++){
                arr[i][j] = str.charAt(j);
                if(arr[i][j] =='1'){
                    vis[i][j] = true;
                }
            }
        }
        
        vis[0][0] = true;
        res = 0;
        buffer = new StringBuffer();
        
        bfs();
//        System.out.println(res -1);

        cin.close();
    }
}

posted on 2020-04-23 17:28  jwthong  阅读(413)  评论(0)    收藏  举报