BFS:广度优先搜索

点赞再看,养成习惯,您动动手指对原创作者意义非凡🤝
作者@试问道

前言

在笔者大大小小笔试了五六场之后,似乎得到了一点规律,互联网大厂的笔试题集中在两大块,一是dp动态规划,另外一个就是dfs深度优先搜索(也包含回溯问题)了(此处再补充一个——BFS 广度优先搜索)。之前笔试的时候,总是觉得面对这种类型的题目,即使勉强写出来了,也远远没有达到融会贯通的程度,很是苦恼。看了几十篇高赞高评论的总结对比文章后,并没有觉得有什么帮助,我总结了两个原因,第一个原因就是作者讲的太浅了,最然容易理解,却无法实际运用到更加复杂的场景中,另一个原因就是,很多别人得出的宝贵结论,你因为缺少自己的思考和大量题目的训练而无法感同身受,因而无法体会到其中的精妙之处。这里我专门讲一下leetcode中的BFS专题下的题目,试一试能不能总结出一些规律,也给自己练习用。

本文末尾附上了图论的几个经典算法,供感兴趣的读者拓展延伸。

正文

Leetcode127. word Ladder

思路:
利用两个set分别从beginword和endword层序替换成新的一批wordset,直到两个set互相有联通了,返回链长度;或者某个set为空时,返回0.这里用了双向BFS,会调换beginset和endset

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Set<String> wordSet = new HashSet<String>(wordList);
        if(!wordSet.contains(endWord)) return 0;
        Set<String> beginSet = new HashSet<String>(), endSet = new HashSet<String>();
        beginSet.add(beginWord);
        endSet.add(endWord);
        int len = 1;
        Set<String> vis = new HashSet<String>();

        while(!beginSet.isEmpty()&&!endSet.isEmpty()){
            if(beginSet.size()>endSet.size()){
                Set<String> temp = beginSet;
                beginSet = endSet;
                endSet = temp;
            }

            Set<String> temp = new HashSet<String>();
            for(String word:beginSet){
                char[] chars = word.toCharArray();
                for (int i = 0; i < chars.length; i++) {
                    //每个位置依次替换a-z字母,构造新的word
                    for(char c='a';c<='z';c++){
                        char origin = chars[i];
                        chars[i] = c;
                        String target = String.valueOf(chars);

                        if(endSet.contains(target)){
                            return len+1;
                        }

                        if(!vis.contains(target) && wordSet.contains(target)){
                            temp.add(target);
                            vis.add(target);
                        }
                        chars[i] = origin;
                    }
                }
            }
            beginSet = temp;
            len++;
        }
        return 0;
    }

Leetcode199. Binary Tree right view

public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root==null) return res;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        while(!q.isEmpty()){
            int lastIndex = q.size()-1;
            for (int i = 0; i <= lastIndex; i++) {
                TreeNode node = q.poll();
                if(node.left!=null) q.offer(node.left);
                if(node.right!=null) q.offer(node.right);
                if(i==lastIndex) res.add(node.val);
            }
        }
        return res;
    }

做完以上两题还未进入正餐环节,因为笔试题目考察BFS喜欢结合图或者二维数组来考,如果只停留在以上水平是远远不够的,接下来几题我会挑一些关于图(或二维数组)的类笔试来做。

Leetcode 542. 01 Matrix

这是一道非常典型的bfs题目,极适合于新手练手,快速熟悉操作。

public class Solution {
    public int[][] updateMatrix(int[][] matrix) {
       int m = matrix.length, n = matrix[0].length;
       Queue<int[]> queue = new LinkedList<>();
       boolean[][] vis = new boolean[m][n];
       for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if(matrix[i][j]==0){
                    queue.offer(new int[]{i,j});
                    vis[i][j] = true;
                }
            }
       }
       int[][] dir = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};
       while (!queue.isEmpty()){
           int[] cur = queue.poll();
           for (int i = 0; i < 4; i++) {
               int x = cur[0]+dir[i][0];
               int y = cur[1]+dir[i][1];
               if(x<0 || y<0 || x>=m || y>=n || vis[x][y]) continue;
               matrix[x][y] = matrix[cur[0]][cur[1]]+1;
               vis[x][y] = true;
               queue.offer(new int[]{x,y});
           }
       }
       return matrix;
    }
}

Leetcode 743. Network Delay Time

非常典型且巧妙的邻接表存图方式,大力推荐!

//There are N network nodes, labelled 1 to N. 
//
// Given times, a list of travel times as directed edges times[i] = (u, v, w), w
//here u is the source node, v is the target node, and w is the time it takes for 
//a signal to travel from source to target. 
//
// Now, we send a signal from a certain node K. How long will it take for all no
//des to receive the signal? If it is impossible, return -1. 
//
// 
//
// Example 1: 
//
// 
//
// 
//Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2
//Output: 2
// 
//
// 
//
// Note: 
//
// 
// N will be in the range [1, 100]. 
// K will be in the range [1, N]. 
// The length of times will be in the range [1, 6000]. 
// All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 0 <= w <= 100. 

class Solution {
    public int networkDelayTime(int[][] times, int N, int K) {
    	//作为模板使用
        HashMap<Integer,HashMap<Integer,Integer>> map = new HashMap<>();
        boolean[] vis = new boolean[N+1];
        for (int[] time:times) {
            map.putIfAbsent(time[0],new HashMap<>());
            map.get(time[0]).put(time[1],time[2]);
        }
        //distance and node, ordered by distance to K
        PriorityQueue<int[]> pq = new PriorityQueue<>((o1,o2)->(o1[0]-o2[0]));
        pq.offer(new int[]{0,K});
        int res=0;
        while (!pq.isEmpty()){
            int[] cur = pq.poll();
            int curNode = cur[1];
            int curDist = cur[0];
            if(vis[curNode]) continue;
            vis[curNode] = true;
            //dist always from low to high
            res = curDist;
            N--;
            //if all the nodes are visited, complete
            if(N==0) return res;
            if(map.containsKey(curNode)){
                for (int next : map.get(curNode).keySet()) {
                    pq.offer(new int[]{curDist+map.get(curNode).get(next),next});
                }
            }
        }
        return -1;

    }
}

Leetcode 752. open the lock

一个标记是否访问过的新思路,用set而不是数组,很巧妙

//You have a lock in front of you with 4 circular wheels. Each wheel has 10 slot
//s: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. The wheels can rotate freel
//y and wrap around: for example we can turn '9' to be '0', or '0' to be '9'. Each
// move consists of turning one wheel one slot. 
//
// The lock initially starts at '0000', a string representing the state of the 4
// wheels. 
//
// You are given a list of deadends dead ends, meaning if the lock displays any 
//of these codes, the wheels of the lock will stop turning and you will be unable 
//to open it. 
//
// Given a target representing the value of the wheels that will unlock the lock
//, return the minimum total number of turns required to open the lock, or -1 if i
//t is impossible. 
//
// 
// Example 1: 
//
// 
//Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
//Output: 6
//Explanation:
//A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "12
//01" -> "1202" -> "0202".
//Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would
// be invalid,
//because the wheels of the lock become stuck after the display becomes the dead
// end "0102".
// 
//
// Example 2: 
//
// 
//Input: deadends = ["8888"], target = "0009"
//Output: 1
//Explanation:
//We can turn the last wheel in reverse to move from "0000" -> "0009".
// 
//
// Example 3: 
//
// 
//Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], t
//arget = "8888"
//Output: -1
//Explanation:
//We can't reach the target without getting stuck.
// 
//
// Example 4: 
//
// 
//Input: deadends = ["0000"], target = "8888"
//Output: -1
// 
//
// 
// Constraints: 
//
// 
// 1 <= deadends.length <= 500 
// deadends[i].length == 4 
// target.length == 4 
// target will not be in the list deadends. 
// target and deadends[i] consist of digits only. 

class Solution {
    public int openLock(String[] deadends, String target) {
        Queue<String> q = new LinkedList<>();
        Set<String> deads = new HashSet<String>(Arrays.asList(deadends));
        Set<String> vis = new HashSet<String>();
        q.offer("0000");
        vis.add("0000");
        int cnt = 0;
        while(!q.isEmpty()){
            int size = q.size();
            while(size>0){
                String cur = q.poll();
                if(deads.contains(cur)){
                    size--;
                    continue;
                }
                if(cur.equals(target)) return cnt;
                StringBuilder sb = new StringBuilder(cur);
                for (int i = 0; i < 4; i++) {
                    char c = cur.charAt(i);
                    String s1 = sb.substring(0,i)+(c=='9'?0:c-'0'+1)+sb.substring(i+1);
                    String s2 = sb.substring(0,i)+(c=='0'?9:c-'0'-1)+sb.substring(i+1);
                    if(!vis.contains(s1)&&!deads.contains(s1)){
                        q.offer(s1);
                        vis.add(s1);
                    }
                    if(!vis.contains(s2)&&!deads.contains(s2)){
                        q.offer(s2);
                        vis.add(s2);
                    }
                }
                size--;
            }
            cnt++;
        }
        return -1;
    }
}

Leetcode 787. Cheapest Flights Within K Stops

//There are n cities connected by m flights. Each flight starts from city u and 
//arrives at v with a price w. 
//
// Now given all the cities and flights, together with starting city src and the
// destination dst, your task is to find the cheapest price from src to dst with u
//p to k stops. If there is no such route, output -1. 
//
// 
//Example 1:
//Input: 
//n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
//src = 0, dst = 2, k = 1
//Output: 200
//Explanation: 
//The graph looks like this:
//
//
//The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as mar
//ked red in the picture. 
//
// 
//Example 2:
//Input: 
//n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
//src = 0, dst = 2, k = 0
//Output: 500
//Explanation: 
//The graph looks like this:
//
//
//The cheapest price from city 0 to city 2 with at most 0 stop costs 500, as mar
//ked blue in the picture.
// 
//
// 
// Constraints: 
//
// 
// The number of nodes n will be in range [1, 100], with nodes labeled from 0 to
// n - 1. 
// The size of flights will be in range [0, n * (n - 1) / 2]. 
// The format of each flight will be (src, dst, price). 
// The price of each flight will be in the range [1, 10000]. 
// k is in the range of [0, n - 1]. 
// There will not be any duplicated flights or self cycles. 


//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public static int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
        HashMap<Integer, HashMap<Integer, Integer>> cities = new HashMap<>();
        for (int i = 0; i < flights.length; i++) {
            cities.putIfAbsent(flights[i][0],new HashMap<>());
            cities.get(flights[i][0]).put(flights[i][1],flights[i][2]);
        }
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{src,0});
        int stop = 0, ans = Integer.MAX_VALUE;
        while (!queue.isEmpty()){
            int size = queue.size();
            // System.out.println("queue:"+size);
            while (size-->0){
                int[] cur = queue.poll();
                // System.out.println("cur:"+cur[0]);
                if(cur[0]==dst) ans = Integer.min(ans,cur[1]);
                // System.out.println("ans:"+ans);
                if(!cities.keySet().contains(cur[0])){
                    continue;
                }
                for(int next:cities.get(cur[0]).keySet()){
                    if(cur[1]+cities.get(cur[0]).get(next)<ans){
                        System.out.println("next:"+next);
                        queue.offer(new int[]{next,cur[1]+cities.get(cur[0]).get(next)});
                    }
                }
            }
            // System.out.println("=============");
            if(stop++>K) break;
            // System.out.println("stop:"+stop);
        }
        return (ans==Integer.MAX_VALUE?-1:ans);
    }
}

Leetcode 934. Shortest Bridge(BFS&DFS)

BFS&DFS混合题型

//In a given 2D binary array A, there are two islands. (An island is a 4-directi
//onally connected group of 1s not connected to any other 1s.) 
//
// Now, we may change 0s to 1s so as to connect the two islands together to form
// 1 island. 
//
// Return the smallest number of 0s that must be flipped. (It is guaranteed that
// the answer is at least 1.) 
//
// 
// Example 1: 
// Input: A = [[0,1],[1,0]]
//Output: 1
// Example 2: 
// Input: A = [[0,1,0],[0,0,0],[0,0,1]]
//Output: 2
// Example 3: 
// Input: A = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
//Output: 1
// 
// 
// Constraints: 
//
// 
// 2 <= A.length == A[0].length <= 100 
// A[i][j] == 0 or A[i][j] == 1 

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    final int[][] dirs = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};
    public int shortestBridge(int[][] A) {
        int m = A.length, n = A[0].length;
        boolean[][] vis = new boolean[m][n];
        Queue<int[]> queue = new LinkedList<>();
        boolean flag = false;
        for (int i = 0; i < m; i++) {
            if(flag) break;
            for (int j = 0; j < n; j++) {
                if(A[i][j]==1){
                    dfs(i,j,A,queue,vis,m,n);
                    flag = true;
                    break;
                }
            }
        }

        int level = 0;
        while (!queue.isEmpty()){
            int size = queue.size();
            while(size-->0){
                int[] cur = queue.poll();
                for(int[] dir:dirs){
                    int nx = cur[0]+dir[0];
                    int ny = cur[1]+dir[1];
                    if(nx>=0 && ny>=0 && nx<m && ny<n && !vis[nx][ny]){
                        if(A[nx][ny] == 1) return level;
                        queue.offer(new int[]{nx,ny});
                        vis[nx][ny] = true;
                    }
                }
            }
            level++;
        }
        return -1;
    }


    public void dfs(int x,int y, int[][] A,Queue<int[]> queue, boolean[][] vis, int m, int n){
        if(x<0||y<0||x>=m||y>=n||vis[x][y]||A[x][y]==0) return;
        vis[x][y] = true;
        queue.offer(new int[]{x,y});
        for (int i = 0; i < 4; i++) {
            int nx = x + dirs[i][0];
            int ny = y + dirs[i][1];
            dfs(nx,ny,A,queue,vis,m,n);
        }
    }
}

拼多多0926笔试题第二题 bfs(最短路径)

import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        char[][] map = new char[N][M];
        sc.nextLine();
        int begX = 0, begY = 0;
        for (int i = 0; i < N; i++) {
            char[] chars = sc.nextLine().toCharArray();
            for (int j = 0; j < N; j++) {
                map[i][j] = chars[j];
                if(chars[j]=='T'){
                    begX = i;
                    begY = j;
                }
            }
        }
        boolean[][] vis = new boolean[N][M];
        int[][] dir = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};
        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[]{begX,begY});
        vis[begX][begY] = true;
        int[][] dist = new int[N][M];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                dist[i][j] = 30;
            }
        }
        int minDist = Integer.MAX_VALUE;
        dist[begX][begY] = 0;
        boolean noWay = true;

        while (!q.isEmpty()){
            int[] cur = q.poll();
            if(map[cur[0]][cur[1]]=='X' && dist[cur[0]][cur[1]]<minDist){
                minDist = dist[cur[0]][cur[1]];
                System.out.println(minDist);
                noWay = false;
            }

            for (int i = 0; i < 4; i++) {
                int[] next = new int[2];
                next[0] = cur[0]+dir[i][0];
                next[1] = cur[1]+dir[i][1];
                if(next[0]<0||next[1]<0||next[0]>=N||next[1]>=M||map[next[0]][next[1]]=='1') continue;
                if(!vis[next[0]][next[1]]){
                    q.add(next);
                    vis[next[0]][next[1]] = true;
                    dist[next[0]][next[1]]=Integer.min(dist[next[0]][next[1]],dist[cur[0]][cur[1]]+1);
                }
            }
        }
        if(noWay){
            System.out.println(0);
        }else {
            System.out.println(minDist);
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if(dist[i][j]==minDist && map[i][j]=='X'){
                        System.out.print(i);
                        System.out.print(" ");
                        System.out.print(j);
                        System.out.print(" ");
                    }
                }
            }
        }
    }
}

以上练习就来到一个节点,难度基本达到了笔试级别,就看看能不能领会奥妙,运用自如了。
以下内容关于图论的BFS、SPFA、Dijkstra、FLOYD算法,各自有不同的使用场景和局限。

BFS只适用于无权图,如何改进?

SPFA算法(可用于负权图)

在这里插入图片描述
在这里插入图片描述

堆优化Dijkstra算法(复杂度最低)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Floyd算法

在这里插入图片描述
在这里插入图片描述

图论小结

在这里插入图片描述

以上截图来自北理工ACM课程Slides

©️2020 CSDN 皮肤主题: 我行我“速” 设计师:Amelia_0503 返回首页