代码随想录算法训练营Day56|图论part02

75 阅读7分钟

kama99 岛屿数量 深搜版

题目链接:kamacoder.com/problempage…

文档讲解:www.programmercarl.com/kamacoder/0…

题目

题目描述:

给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

输入描述:

第一行包含两个整数 N, M,表示矩阵的行数和列数。

后续 N 行,每行包含 M 个数字,数字为 1 或者 0。

输出描述:

输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。

输入示例:

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例:

3

思路

本题思路,是当遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能走到的陆地都标记上。

在后续遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。

所以整体是顺序遍历整个地图,对于一个岛屿,用DFS全部标记上。顺序遍历的方向假设为外层从上到下,内层从左到右。

用深搜三部曲来分析:

  1. 确认递归函数和参数:图、当前节点索引x, y
    1. 递归参数:图、当前节点索引x, y
    2. 全局参数:岛屿计数器count,标记数组flag
  2. 确认终止条件:走到不合法的节点
  3. 处理目前搜索节点出发的路径:标记并递归上下左右的陆地
    1. 标记本节点
    2. 如果右侧节点是陆地且未标记,对右侧节点递归
    3. 如果左侧节点是陆地且未标记,对左侧节点递归
    4. 如果上侧节点是陆地且未标记,取上侧节点递归
    5. 如果下侧节点是陆地且未标记,取下侧节点递归

解法

import java.util.*;

public class Main{
	static int count = 0;	
	static boolean[][] flag;	
	static int m;	
	static int n;
	
	public static void main(String args[]) {	
		Scanner scanner = new Scanner(System.in);
		
		m = scanner.nextInt();		
		n = scanner.nextInt();
		
		int[][] graph = new int[m][n];		
		flag = new boolean[m][n];
		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				graph[i][j] = scanner.nextInt();			
			}		
		}
		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				if (graph[i][j] == 1 && !flag[i][j]) {				
					count++;					
					dfs(graph, i, j);				
				}			
			}		
		}
		
		System.out.println(count);	
	}	
	  	
	private static void dfs(int[][] graph, int x, int y) {	
		flag[x][y] = true;		
		if (x+1 < m && graph[x+1][y]==1 && !flag[x+1][y]) {			
			dfs(graph, x+1, y);		
		}
		
		if (x-1 >= 0 && graph[x-1][y]==1 && !flag[x-1][y]) {			
			dfs(graph, x-1, y);		
		}
		
		if (y-1 >= 0 && graph[x][y-1]==1 && !flag[x][y-1]) {		
			dfs(graph, x, y-1);		
		}
		
		if (y+1 < n && graph[x][y+1]==1 && !flag[x][y+1]) {		
			dfs(graph, x, y+1);		
		}	
	}
}

kama99 岛屿数量 广搜版

题目链接:kamacoder.com/problempage…

文档讲解:www.programmercarl.com/kamacoder/0…

题目

题目描述:

给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

输入描述:

第一行包含两个整数 N, M,表示矩阵的行数和列数。

后续 N 行,每行包含 M 个数字,数字为 1 或者 0。

输出描述:

输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。

输入示例:

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例:

3

思路

本题思路,是当遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能走到的陆地都标记上。

在后续遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。

所以整体是顺序遍历整个地图,对于一个岛屿,用BFS全部标记上。顺序遍历的方向假设为外层从上到下,内层从左到右。

BFS思路:

  1. 从一个没有遍历过的节点陆地进入BFS
  2. 新建队列,把初始节点加入队列,节点标记已访问
  3. 当队列非空
    1. 取队列头,把它上下左右所有未访问的陆地节点标记已访问,并加入队列

解法

import java.util.*;

public class Main{
	static int count = 0;	
	static boolean[][] flag;	
	static int m;	
	static int n;
	
	public static void main(String args[]) {	
		Scanner scanner = new Scanner(System.in);		
		m = scanner.nextInt();		
		n = scanner.nextInt();		
		int[][] graph = new int[m][n];		
		flag = new boolean[m][n];		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				graph[i][j] = scanner.nextInt();			
			}		
		}
		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				if (graph[i][j] == 1 && !flag[i][j]) {				
					count++;					
					bfs(graph, i, j);				
				}			
			}		
		}		
		System.out.println(count);	
	}	
	  
	
	private static void bfs(int[][] graph, int a, int b) {	
		Queue<int[]> queue = new LinkedList<>();		
		queue.add(new int[]{a, b});		
		flag[a][b] = true;
		
		while (!queue.isEmpty()) {		
			int[] node = queue.poll();			
			int x = node[0];			
			int y = node[1];
			
			if (x+1 < m && graph[x+1][y]==1 && !flag[x+1][y]) {			
				flag[x+1][y] = true;				
				queue.add(new int[]{x+1,y});				
			}			
			if (x-1 >= 0 && graph[x-1][y]==1 && !flag[x-1][y]) {			
				flag[x-1][y] = true;				
				queue.add(new int[]{x-1,y});				
			}			
			if (y-1 >= 0 && graph[x][y-1]==1 && !flag[x][y-1]) {			
				flag[x][y-1] = true;				
				queue.add(new int[]{x,y-1});				
			}			
			if (y+1 < n && graph[x][y+1]==1 && !flag[x][y+1]) {			
				flag[x][y+1] = true;				
				queue.add(new int[]{x,y+1});			
			}			
		}		
	}
}

kama100 岛屿的最大面积

题目链接:kamacoder.com/problempage…

文档讲解:www.programmercarl.com/kamacoder/0…

题目

题目描述

给定一个由 1(陆地)和 0(水)组成的矩阵,计算岛屿的最大面积。岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。后续 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

输出一个整数,表示岛屿的最大面积。如果不存在岛屿,则输出 0。

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

4

思路

本题思路和上题相似,当遇到一个没有遍历过的节点陆地,就进入遍历流程,摸清此节点可以到达的陆地面积并标记访问过。全程取最大的面积就得到结果。

所以整体是顺序遍历整个地图,对于一个岛屿,用BFS/DFS全部标记上。顺序遍历的方向假设为外层从上到下,内层从左到右。

解法

import java.util.*;

public class Main{
	static int m;	
	static int n;	
	static boolean[][] flag;	
	static int max = 0;	
	static int area = 0;
	
	public static void main(String[] args) {	
		Scanner scanner = new Scanner(System.in);		
		m = scanner.nextInt();		
		n = scanner.nextInt();		
		int[][] graph = new int[m][n];		
		flag = new boolean[m][n];
		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				graph[i][j] = scanner.nextInt();			
			}		
		}
		
		for (int i = 0; i < m; i++) {		
			for (int j = 0; j < n; j++) {			
				if (graph[i][j] == 1 && !flag[i][j]) {					
					area = 1;					
					dfs(graph, i, j);					
					max = Math.max(max, area);				
				}			
			}		
		}		
		System.out.println(max);
	
	}
	
	  
	
	private static void dfs(int[][] graph, int x, int y) {	
		flag[x][y] = true;		
		if (x+1 < m && graph[x+1][y]==1 && !flag[x+1][y]) {		
			area++;			
			dfs(graph, x+1, y);		
		}
		
		if (x-1 >= 0 && graph[x-1][y]==1 && !flag[x-1][y]) {		
			area++;			
			dfs(graph, x-1, y);			
		}
		
		if (y-1 >= 0 && graph[x][y-1]==1 && !flag[x][y-1]) {		
			area++;			
			dfs(graph, x, y-1);		
		}
		
		if (y+1 < n && graph[x][y+1]==1 && !flag[x][y+1]) {		
			area++;			
			dfs(graph, x, y+1);		
		}				  
	}
}

今日收获总结

岛屿问题的中心就是,一个岛,不论从哪个发现,都应该能按照构成岛屿的规则遍历玩这个岛。由此可以得到岛屿的相关信息。