解答代码
解题思路全在代码注释中,本题作者使用bfs方式作答
import java.util.*;
//1:无需package
//2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//T组数据,遍历T次
int T = scan.nextInt();
for(int p = 0; p < T; p++){
//岛屿的行数、列数
int M = scan.nextInt();
int N = scan.nextInt();
//岛屿矩阵
int[][] island = new int[M][N];
//每个点的访问性,如果visited为true,代表该点是已被探明的岛屿
boolean[][] visited = new boolean[M][N];
//记录每个岛屿的第一个点
ArrayList<Pair> firstPoints = new ArrayList<>();
//bfs所用的队列
Queue<Pair> queue = new ArrayDeque<>();
int ans = 0;
//岛屿的寻找,需要向四个方向寻找相邻点
int[] dx = {0,1,0,-1};
int[] dy = {1,0,-1,0};
//岛屿矩阵的录入
for(int i = 0; i < M; i++){
String s = scan.next();
char[] arr = s.toCharArray();
for(int j = 0; j < N; j++) {
island[i][j] = arr[j] - '0';
}
}
//bfs岛屿数量添加
for(int i = 0; i < M; i++){
for(int j = 0; j < N; j++){
if(island[i][j] == 1 && visited[i][j] == false){
ans++;
Pair firstPoint = new Pair(i,j);
firstPoints.add(firstPoint);
queue.offer(firstPoint);
visited[i][j] = true;
while(!queue.isEmpty()){
Pair pair = queue.poll();
for(int k = 0; k < dx.length; k++){
int x = pair.x + dx[k];
int y = pair.y + dy[k];
if(x >= 0 && x < M && y >= 0 && y < N){
if(island[x][y] == 1 && visited[x][y] == false){
queue.offer(new Pair(x,y));
visited[x][y] = true;
}
}
}
}
}
}
}
//根据遍历方式可知,每个岛屿的第一个点有这样的特性
//该点的左和上两个方向上一定是海,且一定不会有该点所在岛屿的其他点
//因此只需要从这两个方向上的海出发,bfs搜索其连接海
//如果搜索时海坐标能到边界,说明这个岛屿不属于其他岛屿的子岛屿
//海域搜索的方向,每个点只要8个方向有一个能出去,就不是别人的子岛
//为什么不是4个方向请参考样例2中的3号岛屿
int[] dxSea = {-1,-1,-1,0,0,1,1,1};
int[] dySea = {1,0,-1,1,-1,1,0,-1};
//子岛的判断,遍历所有岛屿的第一个点
outer:
for(Pair point : firstPoints) {
//海的被访问性记录,对于每个岛屿单独记录
boolean[][] visitedSea = new boolean[M][N];
//如果某个岛的第一个节点本身就在边界,肯定不是子岛,不必判断
if(point.x != 0 && point.x != M-1 && point.y != 0 && point.y != N-1) {
queue.clear();
//从岛屿第一个点的上方开始搜索海域
queue.offer(new Pair(point.x-1, point.y));
while(!queue.isEmpty()) {
Pair temp = queue.poll();
//每个海节点必定会出队,判断出队的节点性质即可
if(temp.x == 0 || temp.y == 0 || temp.x == M-1 || temp.y == N-1) {
//如果有海节点到达边界,说明该岛不是子岛,直接处理下一个节点
continue outer;
}
for(int k = 0; k < dxSea.length; k++) {
int x = temp.x + dxSea[k];
int y = temp.y + dySea[k];
//如果目标节点不越界,并且不是岛屿节点,也不是被访问过的海节点
if(x >= 0 && x < M && y >= 0 && y < N) {
if(visited[x][y] == false) {
if(visitedSea[x][y] == false) {
//将该节点入队,并标记访问过
queue.offer(new Pair(x,y));
visitedSea[x][y] = true;
}
}
}
}
}
queue.clear();
//从岛屿第一个点的左方开始搜索海域,内容同上
queue.offer(new Pair(point.x, point.y-1));
while(!queue.isEmpty()) {
Pair temp = queue.poll();
if(temp.x == 0 || temp.y == 0 || temp.x == M-1 || temp.y == N-1) {
continue outer;
}
for(int k = 0; k < dxSea.length; k++) {
int x = temp.x + dxSea[k];
int y = temp.y + dySea[k];
if(x >= 0 && x < M && y >= 0 && y < N) {
if(visited[x][y] == false) {
if(visitedSea[x][y] == false) {
queue.offer(new Pair(x,y));
visitedSea[x][y] = true;
}
}
}
}
}
//如果所有海节点已经搜索完了,没有找到出路到达边界,说明是子岛屿,岛屿数量-1
ans--;
}
}
System.out.println(ans);
}
//在此输入您的代码...
scan.close();
}
}
class Pair{
int x;
int y;
public Pair(int x, int y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Pair [x=" + x + ", y=" + y + "]";
}
}