“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情”
昨天是深度优先搜索,现在让我们来对广度优先搜索也来做一下了解
广度优先搜索
是遍历树或者图的另一种基础算法,原理是从根节点开始,辐射状的进行遍历,这里可能还没有一个很具体的印象,那么我举例说明
例如一棵有三层的满二叉树,节点依次以1、2……6、7排列,如图
那么它的广度优先搜索序列就是1,2,3,4,5,6,7(本质上与层序遍历相差不大)
而深度优先搜索是1,2,4,5,3,6,7
到这里应该是懂了区分深度优先搜索和广度优先搜索,那么来个题目看看
题目
实现
层序遍历肯定是用广度优先搜索方法解决,所以这里没有写思路,现在的问题就是广度优先搜索应该怎么实现。常见的一种方法是队列式广度优先搜索。具体来讲,就是对一个一开始放入根节点的队列进行遍历,若有一个节点出队,则把该节点的所有子节点都加入队列中去,这样就可以保证是辐射状遍历,当队列空了,也就是遍历完所有叶子节点,就搜索结束。
代码如下
/*
//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;
}
}
思路
用广度优先搜索来解这道题的话,我是想遍历整个矩阵,若找到陆地就把该陆地作为根节点进行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;
}
}