Given a 2D grid
consists of 0s
(land) and 1s
(water). An island is a maximal 4-directionally connected group of 0s
and a closed island is an island totally (all left, top, right, bottom) surrounded by 1s.
Return the number of closed islands.
Example 1:
Input: grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]]
Output: 2
Explanation:
Islands in gray are closed because they are completely surrounded by water (group of 1s).
Example 2:
Input: grid = [[0,0,1,0,0],[0,1,0,1,0],[0,1,1,1,0]]
Output: 1
Example 3:
Input: grid = [[1,1,1,1,1,1,1],
[1,0,0,0,0,0,1],
[1,0,1,1,1,0,1],
[1,0,1,0,1,0,1],
[1,0,1,1,1,0,1],
[1,0,0,0,0,0,1],
[1,1,1,1,1,1,1]]
Output: 2
Constraints:
1 <= grid.length, grid[0].length <= 100
0 <= grid[i][j] <=1
這道題給了一個只包含0和1的二維數組 grid,說是0代表陸地,1代表海洋,現在定義了被海洋上下左右包圍的陸地為島嶼,現在問有多少個島嶼,注意島嶼必須被海洋完全包圍,和邊界相連的陸地不算是島嶼。既然島嶼是多個為0相連而形成的,那么肯定是要用 BFS 或 DFS 來找到連通區域的,難點是怎么確定找到的連通區域是不是一個島嶼,關鍵在於若某個連通區域和邊界相連了,則其就不是島嶼了。我們可以反過來操作一下,首先把所有和邊界相連的連通區域都找出來並標記,這樣之后再找到的連通區域就一定是島嶼了。所以先遍歷一遍數組,遇到邊界上的陸地,則開始 DFS 遍歷,並標記連通區域,完成了之后,再次遍歷一遍數組,遇到邊界上的陸地,則開始 DFS 遍歷,並標記連通區域,此時找到一個連通區域之后就可以增加島嶼的個數了,參見代碼如下:
解法一:
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int res = 0, m = grid.size(), n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if ((i == 0 || i == m - 1 || j == 0 || j == n - 1) && grid[i][j] == 0) {
dfs(grid, i, j);
}
}
}
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] != 0) continue;
dfs(grid, i, j);
++res;
}
}
return res;
}
void dfs(vector<vector<int>>& grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || i >= m || j < 0 || j >= n || 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);
}
};
我們也可以只遍歷一次,在遞歸函數里做些改動從而區分是否為島嶼,此時讓 dfs 函數具有返回值,返回0表示不是島嶼,返回1表示是島嶼。在遞歸函數中,若越界了,則返回0,表示直接跟邊界相連了,肯定不是島嶼,否則若當前數值大於0了,表示要么遇到海洋了,要么是之前已經遍歷過了,返回1。否則標記當前位置為2(標記成1也行,沒太大影響),然后對四個鄰居位置分別調用遞歸,並把四個結果乘起來,這里的乘法操作是精髓,因為只要其中有一個是0(遇到邊界了),結果就是0了,表示不是島嶼。這里的相乘操作也可以替換為位運算的相'與'的操作 &
,但注意一定不能用邏輯運算的'且'操作 &&
,因為這個會短路后面的遞歸調用,從而可能導致連通區域無法被完全標記,參見代碼如下:
解法二:
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int res = 0, m = grid.size(), n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 0) res += dfs(grid, i, j);
}
}
return res;
}
int dfs(vector<vector<int>>& grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || i >= m || j < 0 || j >= n) return 0;
if (grid[i][j] > 0) return 1;
grid[i][j] = 2;
return dfs(grid, i + 1, j) * dfs(grid, i - 1, j) * dfs(grid, i, j + 1) * dfs(grid, i, j - 1);
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1254
參考資料:
https://leetcode.com/problems/number-of-closed-islands/