【LeetCode刷题笔记】(八)bfs

189 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情 这也是第41篇文章

前言

上一篇文章中我提到了深度优先搜索,那也必然不能漏掉广度优先搜索!很多和图、树相关的问题只要能用dfs做,也肯定可以用bfs做,反之亦然。这两种算法思想各有千秋,看读者更习惯用哪个了。

dfs与bfs的一些比较

  • 深度优先搜索基于栈,而广度优先搜索基于队列
  • 算法名字也说明了:dfs讲究深度,给定一棵树,该算法会从根节点开始,直到到叶子结点后才遍历其他路径;bfs则是每到一层,都将该层遍历完再去下一层。

(印象中我用dfs比较多,但也不是没用bfs写过题,但我在力扣题单搜的时候,写着bfs标签的题我都是用dfs或者递归之类的方法做的。。我也不知道为什么。。搜了20分钟什么都没搜出来,最后求助知乎和CSDN,找到了几题经典题,一看我也刚好写过bfs的解法)

剑指 Offer II 116. 省份数量

题目

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

思路

这里体现了典型的bfs实现。每次将当前元素入队,然后将已有元素逐个出队并遍历。

代码实现

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int cities=isConnected.length;
        boolean[] visited=new boolean[cities];
        int provinces=0;
        Queue<Integer> queue=new LinkedList<Integer>();
        for (int i=0;i<cities;i++) {
            if (!visited[i]){
                queue.offer(i);
                while (!queue.isEmpty()) {
                    int j=queue.poll();
                    visited[j]=true;
                    for (int k=0; k<cities;k++) {
                        if (isConnected[j][k]==1&&!visited[k]) {
                            queue.offer(k);
                        }
                    }
                }
                provinces++;
            }
        }
        return provinces;
    }
}

公交路线

题目

给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。

  • 例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... 这样的车站路线行驶。

现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。

求出 最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。

思路

这题是道困难题,bfs只是其中一步,它关键是要建图,求最短路径,找最短路径。

代码实现(附部分注释)

class Solution {
    public int numBusesToDestination(int[][] routes, int source, int target) {
        if(source==target) return 0;//起终点相同
        int n=routes.length;//公交路线的数量
        //两条线路之间是否有公共站点
        boolean [][] edges=new boolean [n][n];
        //site:站点  list:经过该站点的公交车列表
        Map<Integer,List<Integer>> staTobus=new HashMap<>();
        //第一层循环:第i辆公交车
        for(int i=0;i<n;i++){
            for(int site:routes[i]){
                List<Integer> list=staTobus.getOrDefault(site,new ArrayList<>());
                for(int j:list){
                    edges[i][j]=edges[j][i]=true;
                }
                list.add(i);
                staTobus.put(site,list);
            }
        }

        //求最短距离
        int [] dis=new int [n];
        //初始化数组
        Arrays.fill(dis,-1);
        //有多条线路都可经过起点,先将它们全部入队,
        Deque<Integer> queue=new ArrayDeque<>();
        for(int bus:staTobus.getOrDefault(source,new ArrayList<>())){
            queue.offer(bus);
            dis[bus]=1;
        }

        while(!queue.isEmpty()){
            int x=queue.poll();
            for(int i=0;i<n;i++){
                if(edges[x][i]&&dis[i]==-1){
                    queue.offer(i);
                    dis[i]=dis[x]+1;
                }
            }
        }

        //找最短路径
        int min=Integer.MAX_VALUE;
        for(int bus:staTobus.getOrDefault(target,new ArrayList<>())){
            if(dis[bus]!=-1){
                min=Math.min(min,dis[bus]);
            }
        }
        return min==Integer.MAX_VALUE?-1:min;
    }
}