算法训练#6:广度优先搜索

38 阅读3分钟

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

昨天是深度优先搜索,现在让我们来对广度优先搜索也来做一下了解

广度优先搜索

是遍历树或者图的另一种基础算法,原理是从根节点开始,辐射状的进行遍历,这里可能还没有一个很具体的印象,那么我举例说明

例如一棵有三层的满二叉树,节点依次以1、2……6、7排列,如图 image.png 那么它的广度优先搜索序列就是1,2,3,4,5,6,7(本质上与层序遍历相差不大) 而深度优先搜索是1,2,4,5,3,6,7

到这里应该是懂了区分深度优先搜索和广度优先搜索,那么来个题目看看

题目

429. N 叉树的层序遍历

实现

层序遍历肯定是用广度优先搜索方法解决,所以这里没有写思路,现在的问题就是广度优先搜索应该怎么实现。常见的一种方法是队列式广度优先搜索。具体来讲,就是对一个一开始放入根节点的队列进行遍历,若有一个节点出队,则把该节点的所有子节点都加入队列中去,这样就可以保证是辐射状遍历,当队列空了,也就是遍历完所有叶子节点,就搜索结束。

代码如下

/*
//Node的定义,我把构造方法去掉了,因为基本没用上,所以只保留对代码理解有影响的部分
class Node {
    public int val;
    public List<Node> children;
};
*/

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        //存放遍历序列,外面的list是整棵树的,里面的list是每一层所有节点的
        List<List<Integer>> ans=new ArrayList<List<Integer>>();
        if(root==null) return ans;//判空
        //活节点队列(出队的节点若有子节点就加入这里的队尾)
        List<Node> available=new ArrayList<Node>();
        //初始化
        available.add(root);
        //这里加个null是因为要对层数进行划分,
        available.add(null);
        List<Integer> level=new ArrayList<Integer>();//存一层的list
        while(!available.isEmpty()){//若队空遍历结束
            Node now=available.remove(0);//节点出队
            if(now!=null){//若不为null,则把子节点加入活结点队列,把该节点加入level
                level.add(now.val);
                for(Node i:now.children){
                    available.add(i);
                }
            }else if(!available.isEmpty()){//到了分割一层节点的null
                ans.add(level);
                //这里的重新初始化在我之前的文章有说
                level=new ArrayList<Integer>();
                available.add(null);
            }else {//到了最后一层
                ans.add(level);
            }
        }
        return ans;
    }
}

1020. 飞地的数量

思路

用广度优先搜索来解这道题的话,我是想遍历整个矩阵,若找到陆地就把该陆地作为根节点进行bfs,若遍历不到边界则为飞地。

思路比较简单,但有一些问题需要在代码里解决

代码实现

class Solution {
    public int numEnclaves(int[][] grid) {
        //矩阵长宽
        int m = grid.length;
        int n = grid[0].length;
        
        int[][] flag = new int[m][n];//判定结果若为0则是飞地,为1则不是
        int[][] f = new int[m][n];//是否入过队(也就是是否遍历过)
        Queue<int[]> x = new LinkedList<int[]>();
        Queue<int[]> y = new LinkedList<int[]>();//存储好一次判断的所有1的坐标(存好一整块陆地)
        for (int i = 0; i < m; i++) {//数组初始化
            for (int j = 0; j < n; j++) {
                //-1则为什么操作都没有经历过,也没有对是否飞地进行判断
                flag[i][j] = -1;
                f[i][j] = -1;
            }
        }
        //
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1 && f[i][j] == -1) {//找到没遍历过的陆地
                    x.add(new int[]{i, j});
                    boolean a = false;
                    while (!x.isEmpty()) {//开始遍历
                        int xx = x.peek()[0];
                        int yy = x.peek()[1];
                        if (xx == 0 || xx == m - 1 || yy == 0 || yy == n - 1) {
                            a = true;//在边界就不是飞地
                        }
                        y.add(new int[]{xx, yy});
                        f[xx][yy] = 1;
                        x.remove();
                        //找四个方向的节点入队,若访问过的就不重复添加
                        if (xx + 1 < m && grid[xx + 1][yy] == 1 && f[xx + 1][yy] != 1) {
                            x.add(new int[]{xx + 1, yy});
                        }
                        if (xx - 1 >= 0 && grid[xx - 1][yy] == 1 && f[xx - 1][yy] != 1) {
                            x.add(new int[]{xx - 1, yy});
                        }
                        if (yy - 1 >= 0 && grid[xx][yy - 1] == 1 && f[xx][yy - 1] != 1) {
                            x.add(new int[]{xx, yy - 1});
                        }
                        if (yy + 1 < n && grid[xx][yy + 1] == 1 && f[xx][yy + 1] != 1) {
                            x.add(new int[]{xx, yy + 1});
                        }
                    }
                    
                    int ans = -1;
                    //判断这一块陆地是不是飞地,若是则要将这陆地对应的flag矩阵位置都赋值1,若不是则赋值为0
                    if (a) {
                        ans = 1;
                    } else {
                        ans = 0;
                    }
                    while (!y.isEmpty()) {
                        int xx = y.peek()[0];
                        int yy = y.peek()[1];
                        y.remove();
                        flag[xx][yy] = ans;
                    }
                }
            }
        }
        //数不是飞地的陆地数量
        int count = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (flag[i][j] == 0)
                    count++;
            }
        }
        return count;
    }
}