LeetCode463岛屿的周长islandPerimeter
题目
给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
示例 1:
输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边
示例 2:输入:grid = [[1]]
输出:4
示例 3:输入:grid = [[1,0]]
输出:4提示:
row == grid.length
col == grid[i].length
1 <= row, col <= 100
grid[i][j] 为 0 或 1
解法一、dfs(深度优先搜索)
思路与算法
我们也可以将方法一改成深度优先搜索遍历的方式,此时遍历的方式可扩展至统计多个岛屿各自的周长。需要注意的是为了防止陆地格子在深度优先搜索中被重复遍历导致死循环,我们需要将遍历过的陆地格子标记为已经遍历过,下面的代码中我们设定值为 2 的格子为已经遍历过的陆地格子。
public class Solution{
/*
* 深度优先算法
* 1、遍历数组,从小岛的某个位置开始深度优先搜索,且搜索范围仅仅在小岛范围内
* 2、明确边长sum++的条件,下一次遍历的格子脚标超出小岛脚标范围,即下一次遍历的格子脚标越界或格子内值为0
* 3、开始搜索的点标记(表示已经检索),之后每次搜索后的值都要进行标记*/
//下、右、上、左
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
int sum;
int m;//数组行数
int n;//数组列数
public int islandPerimeter(int[][] grid) {
m = grid.length;
n = grid[0].length;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
grid[i][j]=2;
dist(grid, i, j);
}
}
}
return sum;
}
public void dist(int[][] grid, int i, int j) {
for (int k = 0; k < 4; k++) {
int mx = i + dx[k];
int my = j + dy[k];
boolean tandition=mx >= 0 && mx < m && my >= 0 && my < n;
if (mx == -1 || my == -1 || mx == m || my == n ||(tandition&&grid[mx][my] == 0)) sum++;
if (tandition && grid[mx][my] == 1) {
grid[mx][my] = 2;
dist(grid, mx, my);
}
}
}
}
复杂度分析
- 时间复杂度:O(nm),其中 n 为网格的高度,m 为网格的宽度。每个格子至多会被遍历一次,因此总时间复杂度为 O(nm)。
- 空间复杂度:O(nm)深度优先搜索复杂度取决于递归的栈空间,而栈空间最坏情况下会达到 O(nm)。
解法二、迭代
思路于算法
一块土地原则上会带来 4 个周长,但岛上的土地存在接壤,每一条接壤,会减掉 2 个边长。
所以,总周长 = 4 * 土地个数 - 2 * 接壤边的条数。
遍历矩阵,遍历到土地,就 land++,如果它的右/下边也是土地,则 border++,遍历结束后代入公式。
class Solution{
public int islandPerimeter(int[][] grid) {
int land = 0; // 土地个数
int border = 0; // 接壤边界的条数
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
land++;
if (i < grid.length - 1 && grid[i + 1][j] == 1) {
border++;
}
if (j < grid[0].length - 1 && grid[i][j + 1] == 1) {
border++;
}
}
}
}
return 4 * land - 2 * border;
}
}
复杂度分析
时间复杂度:O(nm),其中 n 为网格的高度,m 为网格的宽度。我们需要遍历每个格子,每个格子要看其右和下个格子是否为岛屿,因此总时间复杂度为 O(2nm)=O(nm)。
空间复杂度:O(1)只需要常数空间存放若干变量。
总结
DFS 从一个点,向四周扩散,目标是遇到矩阵边界或海水,它们是答案已知的 base case,是位于递归树底部的 case,是递归的终止条件。
从上而下递归调用,随着递归的出栈,子问题的解自下而上地返回,最后得出大问题的解。