岛屿的最大面积
- 题目
- 函数原型
- 边界判断
- 算法设计:求最大的联通分量个数
- 算法设计:Flood Fill算法
- 算法设计:并查集
题目
给定一个包含了一些 0
和 1
的非空二维数组 grid
。
一个 岛屿 是由一些相邻的 1
(代表土地) 构成的组合,这里的「相邻」要求两个 1
必须在水平或者竖直方向上相邻。你可以假设 grid
的四个边缘都被 0
(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0
。)
示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6
。注意答案不应该是 11
,因为岛屿只能包含水平或垂直的四个方向的 1
。
示例 2:
[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0
。
函数原型
C的函数原型:
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){}
-
grid
:二维数组 -
gridSize
:二维数组的行数 -
gridColSize
:二维数组的列数,但是int*
,所以取值的时候用*gridColSize
边界判断
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
if( grid == NULL || gridSize == 0 || gridColSize == NULL || *gridColSize == 0 )
return 0;
}
算法设计:求最大的联通分量个数
思路:用 DFS or BFS 求联通分量的个数,最大的那个就是岛屿的最大面积。
- 四联通探索与之相连的每一个土地(以及与这些土地相连的土地),那么探索过的土地总数将是该连通形状的面积。
- 为了确保不会多次访问同一土地,我们每次经过一块土地时,将这块土地的值置为
0
。 - 一般我们会用
visited[]
记录,但这里我们可以直接利用grid[]
,初始陆地是1
,遍历如果见到陆地,就dfs
,在dfs
过程中,每遍历到一个陆地,就设置为0
,这样grid[]
为1
的那些点就是要继续遍历的点。
int dfs(int** grid, int gridSize, int* gridColSize, int i, int j) {
if (i < 0 || j < 0 || i == gridSize || j == *gridColSize || grid[i][j] == 0) // 递归结束条件
return 0;
grid[i][j] = 0;
int ans = 1;
int x[4] = {0, -1, 0, 1};
int y[4] = {-1, 0, 1, 0};
// 四联通,如 (x[0], y[0]) -> (0, -1) -> 南,按次序依次是:南、西、北、东。
for (int k = 0; k < 4; ++k) { // 四个方向都遍历一次
ans += dfs(grid, gridSize, gridColSize, i + x[k], j + y[k]);
}
return ans;
}
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
if( grid == NULL || gridSize == 0 || gridColSize == NULL || *gridColSize == 0 )
return 0;
int ans = 0;
for (int i = 0; i < gridSize; ++i) {
for (int j = 0; j < *gridColSize; ++j) {
int t = dfs(grid, gridSize, gridColSize, i, j);
ans = (ans > t ? ans : t); // dfs() 返回的是联通分量的个数,这里取最大值
}
}
return ans;
}
最大的联通分量个数的复杂度:
- 时间复杂度:
- 空间复杂度:
算法设计:Flood Fill算法
Flood Fill:从某一点开始,按照某种逻辑遍历不断查找邻点。
- 想象一个二维数组,从左上角
(0,0)
开始遍历,直到遍历到右下角(n-1, n-1)
。 - 按照某种逻辑遍历,可以选
DFS
、BFS
等,这里采用DFS
。 - 如果是访问过的邻点,就染色,代表已经访问过来。
- 一般我们会用
visited[]
记录,但这里我们可以直接利用grid[]
,初始陆地是1
,遍历如果见到陆地,就dfs
,在dfs
过程中,每遍历到一个陆地,就设置为0
,这样grid[]
为1
的那些点就是要继续遍历的点。
int dfs(int** grid, int gridSize, int* gridColSize, int x, int y) {
if (x < 0 || y < 0 || x == gridSize || y == *gridColSize || grid[x][y] == 0) // 递归结束条件:出界判断。
return 0;
grid[x][y] = 0; // 表示已访问
int ans = 1;
// 一点点可优化的地方,对数组进行索引也会产生性能开销,可以省了 `x[]`、`y[]` 。
ans += dfs(grid, gridSize, gridColSize, x - 1, y); // 西
ans += dfs(grid, gridSize, gridColSize, x, y + 1); // 北
ans += dfs(grid, gridSize, gridColSize, x + 1, y); // 东
ans += dfs(grid, gridSize, gridColSize, x, y - 1); // 南
return ans;
}
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
if( grid == NULL || gridSize == 0 || gridColSize == NULL || *gridColSize == 0 )
return 0;
int ans = 0;
for (int i = 0; i < gridSize; ++i) {
for (int j = 0; j < *gridColSize; ++j) {
int t = dfs(grid, gridSize, gridColSize, i, j);
ans = (ans > t ? ans : t); // dfs() 返回的是联通分量的个数,这里取最大值
}
}
return ans;
}
为了可读性,可以优化 dfs()
的接口参数。
// 引入一个结构体,减少递归接口的参数
struct _graph{
int** _grid;
int* _gridColSize;
int _gridSize;
} g;
int dfs(int x, int y){
if (x < 0 || y < 0 || x == g._gridSize || y == (*g._gridColSize) || g._grid[x][y] == 0) return 0;
g._grid[x][y] = 0; // 表示已访问
int ans = 1;
ans += dfs(x - 1, y);
ans += dfs(x, y + 1);
ans += dfs(x + 1, y);
ans += dfs(x, y - 1);
return ans;
}
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
if( grid == NULL || gridSize == 0 || gridColSize == NULL || *gridColSize == 0 )
return 0;
/* 赋值,可减少递归参数 */
g._grid = grid;
g._gridColSize = gridColSize;
g._gridSize = gridSize;
int ans = 0;
for (int i = 0; i < gridSize; ++i) {
for (int j = 0; j < *gridColSize; ++j) {
int t = dfs(i, j);
ans = (ans > t ? ans : t); // dfs() 返回的是联通分量的个数,这里取最大值
}
}
return ans;
}
Flood Fill算法的复杂度:
- 时间复杂度:
- 空间复杂度:
算法设计:并查集
Floodfill 算法的本质是,在一个二维数组中,求解连通分量的问题。实际上,这个问题,也可以非常容易的,使用并查集解决。