DFS专题:力扣岛屿问题
开篇
每次做到DFS相关的题目都是直接跳过。蓝桥杯过后痛定思痛,好好学习一下DFS和BFS。先从DFS开始吧。
参考题解:nettee:岛屿类问题的通用解法、DFS 遍历框架
题目链接: 200.岛屿数量
463.岛屿的周长
695.岛屿的最大面积
827.最大人工岛
1905.统计子岛屿
1254.统计封闭岛屿的数目
1568.使陆地分离的最小天数
1020.飞地的数量
419.甲板上的军舰
733.图像渲染
一、岛屿数量
题目链接: 200.岛屿数量
题目描述
代码思路
使用for对每一个网格点进行判断,如果遇到未搜索过的’1’,则使岛屿数加一,并利用dfs将与其相连的‘1’都进行标记,确保每次搜索到1都是一个新的岛屿。
代码纯享版
class Solution {
public int numIslands(char[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == '1'){
sum++;
dfs(grid, i, j);
}
}
}
return sum;
}
void dfs(char[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != '1'){
return;
}
grid[i][j] = '2';
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
代码逐行解析版
class Solution {
public int numIslands(char[][] grid) {
int len = grid.length; //岛屿长度
int wide = grid[0].length; //岛屿宽度
int sum = 0; //岛屿总数
//遍历岛屿每一个位置
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == '1'){ //如果为陆地
sum++; //岛屿总数加一
dfs(grid, i, j); //dfs
}
}
}
return sum;
}
//深度搜索所有连接在一起的'1',将其变为'2'
//grid[i][j] == '0' 水
//grid[i][j] == '1' 未搜索的陆地
//grid[i][j] == '2' 已搜索的陆地
void dfs(char[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
//排除2种情况
//1.超出网格范围的搜索
//2.不是未搜索的陆地
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != '1'){
return;
}
grid[i][j] = '2'; //将grid[i][j] == '1'的情况标记为'2'
//上下左右四个方位的搜索
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
二、岛屿的周长
题目链接: 463.岛屿的周长
题目描述
代码思路
利用for循环遍历到一块陆地后,对这块陆地进行dfs搜索,遇到海洋或超出区域的部分,则周长加一;遇到未搜索过的陆地,则标记为搜索过;遇到搜索过的陆地,直接返回。
代码纯享版
class Solution {
public int area = 0;
public int islandPerimeter(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int i = 0, j = 0;
int judge = 1;
for(i = 0; i < len; i++){
for(j = 0; j < wide; j++){
if(grid[i][j] == 1){
dfs(grid, i , j);
return area;
}
}
}
return 0;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] == 0){
area++;
return;
}
if(grid[i][j] == 2){
return;
}
grid[i][j] = 2;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
代码逐行解析版
class Solution {
public int area = 0; //设周长为公共变量
public int islandPerimeter(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){ //碰到陆地,进行dfs
dfs(grid, i , j);
return area; //只有一块岛屿,所以搜索一次后即可返回周长
}
}
}
return 0; //没找到岛屿,返回0
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
//由题目图可知,搜索到超出区域范围或海洋位置的,周长加一
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] == 0){
area++;
return;
}
if(grid[i][j] == 2){ //已搜索过的直接返回
return;
}
grid[i][j] = 2; //把未搜索过的陆地标记为2:已搜索过的陆地
//对上下左右进行搜索
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
大佬相似题解
主要区别是没有设置周长变量为公共变量,而是在递归中用return来返回计算结果,感觉更高级
public int islandPerimeter(int[][] grid) {
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 1) {
// 题目限制只有一个岛屿,计算一个即可
return dfs(grid, r, c);
}
}
}
return 0;
}
int dfs(int[][] grid, int r, int c) {
// 函数因为「坐标 (r, c) 超出网格范围」返回,对应一条黄色的边
if (!inArea(grid, r, c)) {
return 1;
}
// 函数因为「当前格子是海洋格子」返回,对应一条蓝色的边
if (grid[r][c] == 0) {
return 1;
}
// 函数因为「当前格子是已遍历的陆地格子」返回,和周长没关系
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return dfs(grid, r - 1, c)
+ dfs(grid, r + 1, c)
+ dfs(grid, r, c - 1)
+ dfs(grid, r, c + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length
&& 0 <= c && c < grid[0].length;
}
三、岛屿的最大面积
题目链接: 695.岛屿的最大面积
题目描述
代码思路
利用for循环遍历到一块陆地后,对这块陆地进行dfs搜索,遇到海洋或超出区域的部分,则周长加一;遇到未搜索过的陆地,则标记为搜索过;遇到搜索过的陆地,直接返回。
代码纯享版
class Solution {
public int area = 0;
public int maxAreaOfIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int max = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){
dfs(grid, i, j);
max = Math.max(max, area);
area = 0;
}
}
}
return max;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
area++;
grid[i][j] = 2;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
代码逐行解析版
class Solution {
public int area = 0; //设岛屿的面积为公共变量
public int maxAreaOfIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int max = 0; //用来统计岛屿最大面积
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){ //遍历每一个单元格
if(grid[i][j] == 1){ //遇到土地
dfs(grid, i, j); //进行dfs
max = Math.max(max, area); //获取岛屿面积最大值
area = 0; //将area重新设为0,方便下一块岛屿面积的计算
}
}
}
return max; //返回岛屿面积最大值
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
//遇到超出的区域或水,直接返回
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
area++; //面积加一
grid[i][j] = 2; //标记为搜索过
//对上下左右区域进行搜索
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
大佬相似题解
主要区别是没有设置周长变量为公共变量,而是在递归中用return来返回计算面积结果
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 1) {
int a = area(grid, r, c);
res = Math.max(res, a);
}
}
}
return res;
}
int area(int[][] grid, int r, int c) {
if (!inArea(grid, r, c)) {
return 0;
}
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return 1
+ area(grid, r - 1, c)
+ area(grid, r + 1, c)
+ area(grid, r, c - 1)
+ area(grid, r, c + 1);
}
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length
&& 0 <= c && c < grid[0].length;
}
四、最大人工岛
题目链接: 827.最大人工岛
题目描述
代码思路
这道题作为困难题,比前面三道复杂一点,要进行两次搜索。第一次是利用dfs,统计每一块岛屿的面积。第二次是寻找为0的节点,计算其四个方位岛屿的面积。
代码纯享版
class Solution {
public static int area = 0; //面积
public static Map<Integer, Integer> map = new HashMap(); //记录每块岛屿记号及对应面积
public static int sign = 2; //从2开始标记
public int largestIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int max = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){
dfs(grid, i, j);
map.put(sign, area);
sign++;
area = 0;
}
}
}
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 0){
Set<Integer> set = new HashSet();
if(i + 1 < len){
area += map.getOrDefault(grid[i + 1][j], 0);
set.add(grid[i + 1][j]);
}
if(i - 1 >= 0 && !set.contains(grid[i - 1][j])){
area += map.getOrDefault(grid[i - 1][j], 0);
set.add(grid[i - 1][j]);
}
if(j + 1 < wide && !set.contains(grid[i][j + 1])){
area += map.getOrDefault(grid[i][j + 1], 0);
set.add(grid[i][j + 1]);
}
if(j - 1 >= 0 && !set.contains(grid[i][j - 1])){
area += map.getOrDefault(grid[i][j - 1], 0);
set.add(grid[i][j - 1]);
}
max = Math.max(max, area + 1);
area = 0;
}
}
}
return max == 0 ? len * wide : max;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
area++;
grid[i][j] = sign;
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
代码逐行解析版
class Solution {
public static int area = 0; //面积
public static Map<Integer, Integer> map = new HashMap(); //记录每块岛屿记号及对应面积
public static int sign = 2; //从2开始标记
public int largestIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int max = 0; //记录面积最大值
//第一步:利用dfs统计每一块岛屿的面积
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){ //遍历每一个点
if(grid[i][j] == 1){ //遇到岛屿点
dfs(grid, i, j); //通过dfs计算岛屿面积
map.put(sign, area); //用map来标记岛屿序号并记录对应面积
sign++;
area = 0; //将面积变成0,方便下一个岛屿面积的统计
}
}
}
//第二步:找到每一个为0的点,计算其上下左右四个方位的岛屿面积和
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 0){ //遇到为0的点
Set<Integer> set = new HashSet(); //利用集合防止重复计算同一片岛屿
//对四个方向的岛屿面积进行相加
if(i + 1 < len){
area += map.getOrDefault(grid[i + 1][j], 0);
set.add(grid[i + 1][j]);
}
if(i - 1 >= 0 && !set.contains(grid[i - 1][j])){
area += map.getOrDefault(grid[i - 1][j], 0);
set.add(grid[i - 1][j]);
}
if(j + 1 < wide && !set.contains(grid[i][j + 1])){
area += map.getOrDefault(grid[i][j + 1], 0);
set.add(grid[i][j + 1]);
}
if(j - 1 >= 0 && !set.contains(grid[i][j - 1])){
area += map.getOrDefault(grid[i][j - 1], 0);
set.add(grid[i][j - 1]);
}
max = Math.max(max, area + 1); //取最大值,area+1的加一是将grid[i][j]=0变成1
area = 0;
}
}
}
//如果找不到为0的单元格,证明该矩阵全部都是1,直接返回矩阵面积,否则返回max
return max == 0 ? len * wide : max;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
//排除超出区域的情况和已统计过的情况
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
area++; //面积加一
grid[i][j] = sign; //标记岛屿记号,也使其被标记为统计过
//上下左右四个方位进行搜索
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
五、统计子岛屿
题目链接: 1905.统计子岛屿
题目描述
代码思路
一共两个步骤:第一次for循环利用dfs将grid2的每一块岛屿进行标记并计算总岛屿数目sum;第二次for循环同时将grid1和grid2进行遍历,如果发现未完全包含的岛屿,则令sum-1.最终统计出子岛屿的数目
代码纯享版
class Solution {
public static int sign = 2; //标记岛屿序号
public int countSubIslands(int[][] grid1, int[][] grid2) {
int len = grid2.length;
int wide = grid2[0].length;
int sum = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid2[i][j] == 1){
dfs(grid2, i, j);
sum++;
sign++;
}
}
}
Set<Integer> set = new HashSet();
set.add(0);
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(!set.contains(grid2[i][j]) && grid1[i][j] == 0){
set.add(grid2[i][j]);
sum--;
}
}
}
return sum;
}
void dfs(int[][] grid2, int i, int j){
int len = grid2.length;
int wide = grid2[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid2[i][j] != 1){
return;
}
grid2[i][j] = sign;
dfs(grid2, i + 1, j);
dfs(grid2, i - 1, j);
dfs(grid2, i, j + 1);
dfs(grid2, i, j - 1);
}
}
代码逐行解析版
class Solution {
public static int sign = 2; //标记岛屿序号
public int countSubIslands(int[][] grid1, int[][] grid2) {
int len = grid2.length;
int wide = grid2[0].length;
int sum = 0; //统计子岛屿数量
//第一步:标记grid2中的每块岛屿并计算grid2中岛屿数量
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){ //遍历grid2的点
if(grid2[i][j] == 1){ //遇到陆地
dfs(grid2, i, j); //利用dfs搜索该陆地所在的整个岛屿
sum++; //先算出grid2中岛屿的数量
sign++;
}
}
}
//同时对grid1和grid2的点进行遍历,判断有多少不重合
Set<Integer> set = new HashSet(); //区分哪些点需要遍历,set集合中存在的点:0和已发现没有完全包含的岛屿序号
set.add(0); //0点不需遍历
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(!set.contains(grid2[i][j]) && grid1[i][j] == 0){ //发现新的未完全包含的岛屿序号
set.add(grid2[i][j]); //添加到set集合
sum--; //子岛屿数量减一
}
}
}
return sum; //返回子岛屿数量
}
void dfs(int[][] grid2, int i, int j){
int len = grid2.length;
int wide = grid2[0].length;
//排除超出区域的情况和已搜索过的点
if(i < 0 || i >= len || j < 0 || j >= wide || grid2[i][j] != 1){
return;
}
grid2[i][j] = sign; //标记该点所在岛屿点序号
//对该点四个方位进行搜索
dfs(grid2, i + 1, j);
dfs(grid2, i - 1, j);
dfs(grid2, i, j + 1);
dfs(grid2, i, j - 1);
}
}
六、统计封闭岛屿的数目
题目链接: 1254.统计封闭岛屿的数目
题目描述
代码思路
这道题和核心点:陆地的边界不是搜索区域边界,而是水。所以在dfs中根据边界判断是否为封闭岛屿即可
代码纯享版
class Solution {
public static int judge = 1;
public int closedIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 0){
dfs(grid, i, j);
sum += judge;
judge = 1;
}
}
}
return sum;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide){
judge = 0;
return;
}
if(grid[i][j] != 0){
return;
}
grid[i][j] = 2;
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
代码逐行解析版
class Solution {
public static int judge = 1; //用于判断是否是封闭岛屿,1是,0不是
public int closedIsland(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0; //统计封闭岛屿数量
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 0){ //遇到土地(与前面题目相反,这道题土地上0,水是1)
dfs(grid, i, j); //dfs判断是否为封闭岛屿
sum += judge; //将judge设置为0或1,便于统计数目,不需要额外的判断
judge = 1; //将judge还原为1
}
}
}
return sum; //返回封闭岛屿数目
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide){ //遇到超出区域的,都不是封闭岛屿
judge = 0;
return;
}
if(grid[i][j] != 0){ //遇到水(1)或搜索过(2)直接返回
return;
}
grid[i][j] = 2; //将grid[i][j]==0的点设置为2
//对该点四个方位进行搜索
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
七、使陆地分离的最小天数
题目链接: 1568.使陆地分离的最小天数
题目描述
代码思路
这道题思考后会发现只有0,1,2三种答案。先统计岛屿个数,若大于1,则答案为0;若岛屿个数小于等于1且面积小于等于2,则答案为岛屿面积。排除上述情况后,则遍历判断是否能只修改一个陆地实现隔离的效果,可以返回1,否则返回2.
代码纯享版
class Solution {
public int num = 0; //岛屿的面积
public int num_part = 0; //分离一次后岛屿的面积
public int minDays(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){
sum++;
if(sum == 2) return 0;
dfs(grid, i, j);
}
}
}
if(num <= 2) return num;
else{
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 2){
grid[i][j] = 0;
if(separation(grid) == true) return 1;
grid[i][j] = 2;
}
}
}
}
return 2;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
num++;
grid[i][j] = 2;
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
Boolean separation(int[][] grid){
int len = grid.length;
int wide = grid[0].length;
int judge = 1;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 2){
dfs2(grid, i, j);
if(num_part != num - 1) return true;
else{
judge = 0;
num_part = 0;
for(int k = 0; k < len; k++){
for(int l = 0; l < wide; l++){
if(grid[k][l] == 3) grid[k][l] = 2;
}
}
break;
}
}
if(judge == 0) break;
}
}
return false;
}
void dfs2(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 2){
return;
}
num_part++;
grid[i][j] = 3;
dfs2(grid, i + 1, j);
dfs2(grid, i - 1, j);
dfs2(grid, i, j + 1);
dfs2(grid, i, j - 1);
}
}
代码逐行解析版
class Solution {
public int num = 0; //岛屿的面积
public int num_part = 0; //分离一次后岛屿的面积
public int minDays(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0;
//统计岛屿的个数和岛屿的面积
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){
sum++;
if(sum == 2) return 0; //个数为2,直接返回
dfs(grid, i, j); //dfs计算岛屿面积
}
}
}
//此时已确定岛屿只有一个
if(num <= 2) return num; //若岛屿面积小于等于2,只能全部改为水
//判断只更改一个陆地能否实现隔离(陆地为2,水为0)
else{
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 2){
grid[i][j] = 0; //将一块陆地更改为水
if(separation(grid) == true) return 1; //单独使用separation方法来判断,如果成功,返回1
grid[i][j] = 2;
}
}
}
}
return 2; //发现不能只更改1个陆地,只能返回2
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 1){
return;
}
num++;
grid[i][j] = 2;
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
Boolean separation(int[][] grid){
int len = grid.length;
int wide = grid[0].length;
int judge = 1; //一次更改只需判断判断一次即可,用judge方便退出双层循环
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 2){
dfs2(grid, i, j);//利用dfs2搜索看是否完成隔离
if(num_part != num - 1) return true; //两者不相等说明实现隔离,返回true
//将grid数组、num_part恢复到搜索前到样子,方便下一次搜索
else{
judge = 0;
num_part = 0;
for(int k = 0; k < len; k++){
for(int l = 0; l < wide; l++){
if(grid[k][l] == 3) grid[k][l] = 2;
}
}
break;
}
}
if(judge == 0) break;
}
}
return false;
}
void dfs2(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || grid[i][j] != 2){
return;
}
num_part++;
grid[i][j] = 3;
dfs2(grid, i + 1, j);
dfs2(grid, i - 1, j);
dfs2(grid, i, j + 1);
dfs2(grid, i, j - 1);
}
}
八、飞地的数量
题目链接: 1020.飞地的数量
题目描述
代码思路
代码纯享版
class Solution {
public int judge = 1; //搭配陆地是否接触网格边界,1:接触;0:不接触
public int area = 0; //每块陆地的单元格数量
public int numEnclaves(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0; //满足要求的单元格数量
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){
dfs(grid, i, j);
sum += judge == 1 ? area : 0;
judge = 1;
area = 0;
}
}
}
return sum;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide){
judge = 0;
return;
}
if(grid[i][j] != 1) return;
grid[i][j] = 2;
area++;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
代码逐行解析版
class Solution {
public int judge = 1; //搭配陆地是否接触网格边界,1:接触;0:不接触
public int area = 0; //每块陆地的单元格数量
public int numEnclaves(int[][] grid) {
int len = grid.length;
int wide = grid[0].length;
int sum = 0; //满足要求的单元格总数
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(grid[i][j] == 1){ //遇到陆地
dfs(grid, i, j); //运用dfs,判断是否接触网格边界,并计算该陆地的单元格数量
sum += judge == 1 ? area : 0; //为1时满足题目条件,将陆地单员格加到sum中
//恢复judge和area
judge = 1;
area = 0;
}
}
}
return sum;
}
void dfs(int[][] grid, int i, int j){
int len = grid.length;
int wide = grid[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide){ //接触了网格边界,judge变成0
judge = 0;
return;
}
if(grid[i][j] != 1) return; //不是1时,为海洋或遍历过的陆地
grid[i][j] = 2; //标记为遍历过
area++; //该陆地的单元格加1
//遍历该单元格四个方位
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
}
十、图像渲染
题目链接:
题目描述
代码思路
简单模版题,每次遇到军舰加一即可
代码纯享版
class Solution {
public int countBattleships(char[][] board) {
int len = board.length;
int wide = board[0].length;
int sum = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(board[i][j] == 'X'){
sum++;
dfs(board, i, j);
}
}
}
return sum;
}
void dfs(char[][] board, int i, int j){
int len = board.length;
int wide = board[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || board[i][j] != 'X'){
return;
}
board[i][j] = '0';
dfs(board, i + 1, j);
dfs(board, i - i, j);
dfs(board, i, j + 1);
dfs(board, i, j - 1);
}
}
代码逐行解析版
class Solution {
public int countBattleships(char[][] board) {
int len = board.length;
int wide = board[0].length;
int sum = 0; //统计战舰的数量
for(int i = 0; i < len; i++){
for(int j = 0; j < wide; j++){
if(board[i][j] == 'X'){ //遇到战舰
sum++; //数量加一
dfs(board, i, j); //dfs搜索整只战舰
}
}
}
return sum; //返回战舰的数量
}
void dfs(char[][] board, int i, int j){
int len = board.length;
int wide = board[0].length;
if(i < 0 || i >= len || j < 0 || j >= wide || board[i][j] != 'X'){ //排除超出范围的区域和'.'区域
return;
}
board[i][j] = '0'; //标记为搜索过
//搜索该位置的四个方位
dfs(board, i + 1, j);
dfs(board, i - i, j);
dfs(board, i, j + 1);
dfs(board, i, j - 1);
}
}
十、图像渲染
题目链接: 733.图像渲染
题目描述
代码思路
从给定的起点开始,进行深度优先搜索。每次搜索到一个方格时,如果其与初始位置的方格颜色相同,就将该方格的颜色更新。注意:因为初始位置的颜色会被修改,所以我们需要保存初始位置的颜色,以便于之后的更新操作。
代码纯享版
class Solution {
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
if(image[sr][sc] != color){
dfs(image, sr, sc, color, image[sr][sc]);
}
return image;
}
void dfs(int[][] image, int i, int j, int color, int firstcolor){
int len = image.length;
int wide = image[0].length;
if(i < 0 || i >= len || j < 0 || j >=wide || image[i][j] != firstcolor){
return;
}
image[i][j] = color;
dfs(image, i + 1, j, color, firstcolor);
dfs(image, i - 1, j, color, firstcolor);
dfs(image, i, j + 1, color, firstcolor);
dfs(image, i, j - 1, color, firstcolor);
}
}
代码逐行解析版
class Solution {
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
if(image[sr][sc] != color){ //起始颜色和目标颜色相同,直接返回原图,不需dfs。具体解释看代码问题的回答
dfs(image, sr, sc, color, image[sr][sc]);
}
return image;
}
//color是修改后的颜色,firstcolor是初始像素的颜色
void dfs(int[][] image, int i, int j, int color, int firstcolor){
int len = image.length;
int wide = image[0].length;
//排除超出区域的情况和无需修改的区域
if(i < 0 || i >= len || j < 0 || j >=wide || image[i][j] != firstcolor){
return;
}
image[i][j] = color; //修改颜色
dfs(image, i + 1, j, color, firstcolor);
dfs(image, i - 1, j, color, firstcolor);
dfs(image, i, j + 1, color, firstcolor);
dfs(image, i, j - 1, color, firstcolor);
}
}
代码有关问题的解释
为什么要在起始颜色和目标颜色相同时,不进行dfs,而是直接返回原图?
引用力扣中大佬的解释: