宽度优先搜索BFS

306 阅读1分钟

宽度优先搜索BFS

Snipaste_2023-03-08_18-18-13.png

  • 搜索方式:一层一层搜索
  • 数据结构:队列queue
  • 空间:O(2^h^)
  • 搜索到的一定是最短路

Snipaste_2023-03-13_17-54-33.png

练习

01 走迷宫

  • 题目

Snipaste_2023-03-13_18-41-49.png

  • 题解
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 110;
    public static int[][] g = new int[N][N];  //存地图
    public static int[][] d = new int[N][N];  //存走过的路
    //public static Pair[][] prev = new Pair[N][N]; //记录路径
    public static int n, m;

    //存点
    public static class Pair {
        public int first;
        public int second;

        public Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        String[] str1 = br.readLine().split(" ");
        n = Integer.parseInt(str1[0]);
        m = Integer.parseInt(str1[1]);
        for (int i = 0; i < n; i++) {
            String[] str2 = br.readLine().split(" ");
            for (int j = 0; j < m; j++) {
                g[i][j] = Integer.parseInt(str2[j]);
            }
        }

        pw.println(bfs());

        br.close();
        pw.close();
    }

    public static int bfs() {
        //队列
        ArrayDeque<Pair> q = new ArrayDeque<>();
        //初始化走过的路,没走过就为-1
        for (int i = 0; i < N; i++) {
            Arrays.fill(d[i], -1);
        }
        //起点
        d[0][0] = 0;
        //将起点加入队列
        q.offer(new Pair(0, 0));
        //定义每次要走的上下左右四个方向
        int[] dx = new int[]{-1, 0, 1, 0};
        int[] dy = new int[]{0, 1, 0, -1};

        while (q.size() != 0) {
            //弹出队头元素
            Pair t = q.pop();

            //遍历上下左右四个方向
            for (int i = 0; i < 4; i++) {
                int x = t.first + dx[i];
                int y = t.second + dy[i];
                //如果该方向在地图内,并且有路,并且没有走过
                if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) {
                    //走过去,记录到达该位置时的移动次数
                    d[x][y] = d[t.first][t.second] + 1;
                    //该点是从哪个点走过来的,记录一下
                    //prev[x][y] = t;
                    //把该位置添加进队尾
                    q.offer(new Pair(x, y));
                }
            }
        }
        //从终点开始输出路径
        /*int x = n - 1, y = m - 1;
        while (x != 0 || y != 0) {
            System.out.println(x + " " + y);
            Pair t = prev[x][y];
            x = t.first;
            y = t.second;
        }
        System.out.println(x + " " + y);*/
        
        //返回终点记录的移动次数
        return d[n - 1][m - 1];
    }
}

02 八数码

  • 题目

Snipaste_2023-03-15_11-28-37.png

  • 题解1
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        String[] str1 = br.readLine().split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str1.length; i++) {
            sb.append(str1[i]);
        }
        String start = sb.toString();

        pw.println(bfs(start));
        br.close();
        pw.close();
    }

    public static int bfs(String start) {
        //定义结束状态
        String end = "12345678x";
        int[] dx = new int[]{-1, 0, 1, 0};
        int[] dy = new int[]{0, 1, 0, -1};
        ArrayDeque<String> q = new ArrayDeque<>();  //存状态
        Map<String, Integer> d = new HashMap<>();  //存状态和最短多少步到这个状态
        //初始化,把起点加进去
        q.offer(start);
        d.put(start, 0);

        while (!q.isEmpty()) {
            //从队头弹出一个状态
            String t = q.poll();

            //如果这个状态是结束状态,return
            if (t.equals(end)) {
                return d.get(t);
            }

            //获取从初始状态到这个状态所需要的最短步数
            int distance = d.get(t);

            //获取x的一维坐标,将一维坐标变转化成二维坐标
            int k = t.indexOf("x");
            int x = k / 3;
            int y = k % 3;

            //上下左右都遍历一次
            for (int i = 0; i < 4; i++) {
                //走一步后x的新坐标
                int a = x + dx[i];
                int b = y + dy[i];
                //如果新坐标合法
                if (a >= 0 && a < 3 && b >= 0 && b < 3) {
                    //更新状态,将x的新老坐标上的元素互换
                    String newState = swapAndToString(t, k, a * 3 + b);
                    //如果新的状态没有记录过,那就是到这个状态的最短步数,添加进队列和哈希表中
                    if (!d.containsKey(newState)) {
                        d.put(newState, distance + 1);
                        q.offer(newState);
                    }
                }
            }
        }
        return -1;
    }

    public static String swapAndToString(String t, int a, int b) {
        char[] chars = t.toCharArray();

        char tmp = chars[a];
        chars[a] = chars[b];
        chars[b] = tmp;

        StringBuilder sb = new StringBuilder();
        return sb.append(chars).toString();
    }
}
  • 题解2
    • 使用StringBuffer中的setCharAt方法实现字符串中的两个字符交换,比较新颖,但是比题解1的方式慢一丢丢
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        String[] str1 = br.readLine().split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str1.length; i++) {
            sb.append(str1[i]);
        }
        String start = sb.toString();

        pw.println(bfs(start));
        br.close();
        pw.close();
    }

    public static int bfs(String start) {
        //定义结束状态
        String end = "12345678x";
        int[] dx = new int[]{-1, 0, 1, 0};
        int[] dy = new int[]{0, 1, 0, -1};
        ArrayDeque<String> q = new ArrayDeque<>();  //存状态
        Map<String, Integer> d = new HashMap<>();  //存状态和最短多少步到这个状态
        //初始化,把起点加进去
        q.offer(start);
        d.put(start, 0);

        while (!q.isEmpty()) {
            //从队头弹出一个状态
            String t = q.poll();

            //如果这个状态是结束状态,return
            if (t.equals(end)) {
                return d.get(t);
            }

            //获取从初始状态到这个状态所需要的最短步数
            int distance = d.get(t);

            //获取x的一维坐标,将一维坐标变转化成二维坐标
            int k = t.indexOf("x");
            int x = k / 3;
            int y = k % 3;

            //上下左右都遍历一次
            for (int i = 0; i < 4; i++) {
                //走一步后x的新坐标
                int a = x + dx[i];
                int b = y + dy[i];
                //如果新坐标合法
                if (a >= 0 && a < 3 && b >= 0 && b < 3) {
                    //更新状态,将x的新老坐标上的元素互换
                    String newState = swap(t, k, a * 3 + b);
                    //如果新的状态没有记录过,那就是到这个状态的最短步数,添加进队列和哈希表中
                    if (!d.containsKey(newState)) {
                        d.put(newState, distance + 1);
                        q.offer(newState);
                    }
                }
            }
        }
        return -1;
    }

    public static String swap(String t, int a, int b) {
        StringBuffer sb = new StringBuffer(t);
        sb.setCharAt(a, t.charAt(b));
        sb.setCharAt(b, 'x');
        return sb.toString();
    }
}