【dfs】B012_統計封閉島嶼的數目(dfs | bfs == Flood fill)

一、題目描述

有一個二維矩陣 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);
}

複雜度分析

  • 時間複雜度:O(n×m)O(n × m)
  • 空間複雜度:O(n×m)O(n × m)

方法二: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));
      }
    }
  }
}

複雜度分析

  • 時間複雜度:O(n×m)O(n × m)
  • 空間複雜度:O(n×m)O(n × m)

方法三:排除法(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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章