一、題目描述
有一個二維矩陣 grid ,每個位置要麼是陸地(記號爲 0 )要麼是水域(記號爲 1 )。
我們從一塊陸地出發,每次可以往上下左右 4 個方向相鄰區域走,
能走到的所有陸地區域,我們將其稱爲一座「島嶼」。
如果一座島嶼 完全 由水域包圍,即陸地邊緣上下左右所有相鄰區域都是水域,
那麼我們將其稱爲 「封閉島嶼」。
請返回封閉島嶼的數目。
二、題解(Flood fill)
做這種求環繞區域的一般有兩種做法:
- 常規做法:對每一個陸地 land 進行 dfs。
- 如果途中沒有經過 “邊界區域”,則該陸地可算作 1 個封閉島嶼;
- 如果經過“邊界”,則不加入計數 res 中。
- 排除法:想法是先排除掉非封閉島嶼(即邊界的島嶼),先讓邊界的島嶼變爲海洋,然後剩下的島嶼全都是封閉島嶼。
方法一:dfs(常規 2ms)
這題要理解封閉島嶼是什麼意思,如果一個島嶼在 dfs 時沒有遇到邊界,我們認爲這個島嶼是封閉的,如果遇到了邊界,則把 meetEdge 標記爲 true,在 dfs 回來以後,我們對剛剛遍歷的島嶼不進行計數,因爲他是不封閉的。
對於每一個陸地 0,我們從該結點 dfs,並初始化 meetEdge 爲 false 表示
boolean meetEdge;
int res = 0;
public int closedIsland(int[][] grid) {
int m = grid.length, n = grid[0].length;
for (int x = 0; x < m; x++)
for (int y = 0; y < n; y++) {
if (grid[x][y] == 0) {
meetEdge = false;
dfs(grid, x, y);
if (meetEdge == false)
res++;
}
}
return res;
}
// 深搜
private void dfs(int[][] grid, int x, int y) {
if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) {
meetEdge = true;
return;
}
//如果不是邊界但是遇到了海洋。則繼續搜索其他方向
if (grid[x][y] != 0)
return;
grid[x][y] = -1;
dfs(grid, x-1, y);
dfs(grid, x+1, y);
dfs(grid, x, y-1);
dfs(grid, x, y+1);
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法二:bfs(常規 4ms)
思想是一樣的,只不過實現方式不一樣,邏輯簡單。
boolean meetEdge;
int res = 0;
Queue<Pos> queue;
final static int[][] dir = { {0,1},{0,-1},{1,0},{-1,0} };
int m, n;
public int closedIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
queue = new LinkedList<>();
for (int x = 0; x < m; x++)
for (int y = 0; y < n; y++) {
if (grid[x][y] == 0) {
meetEdge = false;
bfs(grid, x, y);
if (meetEdge == false)
res++;
}
}
return res;
}
private void bfs(int[][] grid, int x, int y) {
queue.add(new Pos(x, y));
while (!queue.isEmpty()) {
Pos pos = queue.poll();
if (pos.x == 0 || pos.x == m-1 || pos.y == 0 || pos.y == n-1) {
meetEdge = true;
continue;
//return; 不能立刻return,否則會把島嶼分割爲兩個島嶼
}
for (int i = 0; i < 4; i++) {
int newX = pos.x + dir[i][0];
int newY = pos.y + dir[i][1];
if (newX >= 0 && newX < m && newY >= 0 && newY < n && grid[newX][newY] == 0) {
grid[newX][newY] = 1; //標記爲訪問過
queue.add(new Pos(newX, newY));
}
}
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法三:排除法(2ms)
先排除掉非封閉島嶼(即邊界的島嶼),先讓邊界的島嶼變爲海洋,然後剩下的島嶼全都是封閉島嶼。
int res = 0;
int m, n;
public int closedIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
//處理行
for (int y = 0; y < n; y++) {
if (grid[0][y] == 0) dfs(grid, 0, y);
if (grid[m-1][y] == 0) dfs(grid, m-1, y);
}
//處理列
for (int x = 0; x < m; x++) {
if (grid[x][0] == 0) dfs(grid, x, 0);
if (grid[x][n-1] == 0) dfs(grid, x, n-1);
}
for (int x = 1; x < m; x++)
for (int y = 1; y < n; y++) {
if (grid[x][y] == 0) {
res++;
dfs(grid, x, y);
}
}
return res;
}
//深度優先搜索
private void dfs(int[][] grid, int x, int y) {
if (x < 0 || x >= m || y < 0 || y >= n) {
return;
}
if (grid[x][y] != 0)
return;
grid[x][y] = 1; //淹沒島嶼
dfs(grid, x-1, y);
dfs(grid, x+1, y);
dfs(grid, x, y-1);
dfs(grid, x, y+1);
}