岛屿的最大面积

  • 题目
  • 函数原型
  • 边界判断
  • 算法设计:求最大的联通分量个数
  • 算法设计:Flood Fill算法
  • 算法设计:并查集



 


题目

给定一个包含了一些 01 的非空二维数组 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;
}

最大的联通分量个数的复杂度:

  • 时间复杂度:[695].岛屿的最大面积_二维数组
  • 空间复杂度:[695].岛屿的最大面积_二维数组
     

算法设计:Flood Fill算法

Flood Fill:从某一点开始,按照某种逻辑遍历不断查找邻点。

  • 想象一个二维数组,从左上角(0,0)开始遍历,直到遍历到右下角(n-1, n-1)
  • 按照某种逻辑遍历,可以选 DFSBFS 等,这里采用 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算法的复杂度:

  • 时间复杂度:[695].岛屿的最大面积_二维数组
  • 空间复杂度:[695].岛屿的最大面积_二维数组
     

算法设计:并查集

Floodfill 算法的本质是,在一个二维数组中,求解连通分量的问题。实际上,这个问题,也可以非常容易的,使用并查集解决。