200. Number of Islands I & II
Given a boolean 2D matrix, find the number of islands.
Notice
0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent.
Given graph:
[
[1, 1, 0, 0, 0],
[0, 1, 0, 0, 1],
[0, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 1]
]
return 3
.
分析:
从第一个数到最后一个数,如果遇到1,则island count 加1, 并且不断递归去寻找周围的1。
1 public class Solution { 2 public int numIslands(char[][] grid) { 3 int count = 0; 4 if (grid == null || grid.length == 0) return count; 5 6 int m = grid.length, n = grid[0].length; 7 boolean[][] visited = new boolean[m][n]; 8 9 for (int i = 0; i < m; i++) { 10 for (int j = 0; j < n; j++) { 11 if (!visited[i][j] && grid[i][j] == '1') { 12 count++; 13 flood(i, j, grid, visited); 14 } 15 } 16 } 17 return count; 18 } 19 20 private void flood(int i, int j, char[][] grid, boolean[][] visited) { 21 if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length) return; 22 if (grid[i][j] == '0' || visited[i][j]) return; 23 visited[i][j] = true; 24 flood(i + 1, j, grid, visited); 25 flood(i - 1, j, grid, visited); 26 flood(i, j + 1, grid, visited); 27 flood(i, j - 1, grid, visited); 28 } 29 }
Follow up:
Find the perimeter of the island. Answer: find the # of 1 which is next to 0.
Number of Islands II
A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand operation which turns the water at position (row, col) into a land. Given a list of positions to operate, count the number of islands after each addLand operation. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example:
Given m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]].
Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 >represents land).
0 0 0
0 0 0
0 0 0
Operation #1: addLand(0, 0) turns the water at grid0 into a land.
1 0 0
0 0 0 Number of islands = 1
0 0 0
Operation #2: addLand(0, 1) turns the water at grid0 into a land.
1 1 0
0 0 0 Number of islands = 1
0 0 0
Operation #3: addLand(1, 2) turns the water at grid1 into a land.
1 1 0
0 0 1 Number of islands = 2
0 0 0
Operation #4: addLand(2, 1) turns the water at grid2 into a land.
1 1 0
0 0 1 Number of islands = 3
0 1 0
We return the result as an array: [1, 1, 2, 3]
分析:https://segmentfault.com/a/1190000004197552
很典型的union-find题。因为这里是动态的增加land,要能随时求出有多少个island,最简单的方法就是union-find。我们可以定义一个counter, 每增加一个land, 增加counter, 然后我们搜索那个land邻居区域,发现root不一样的话,意味着可以union, 每union一次,意味着两个island合并成一个,减小counter, 统计最终的counter值,即是增加land后的最终island的个数。
为了减小时间复杂度,代码实现是QuickUnion + Path Compression, Path Compression目的是为了调整树的高度,保持很平的树,而不是越来越高,这样找root不会出现worst case.
1 public class Solution { 2 public List<Integer> numIslands2(int m, int n, int[][] positions) { 3 int[] id = new int[m * n]; // 表示各个index对应的root 4 Arrays.fill(id, -1); // 初始化root为-1,用来标记water, 非-1表示land 5 6 List<Integer> res = new ArrayList<>(); 7 int count = 0; // 记录island的数量 8 int[][] dirs = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; 9 10 for (int i = 0; i < positions.length; i++) { 11 count++; 12 int index = positions[i][0] * n + positions[i][1]; 13 id[index] = index; // root初始化 14 15 for (int j = 0; j < dirs.length; j++) { 16 int x = positions[i][0] + dirs[j][0]; 17 int y = positions[i][1] + dirs[j][1]; 18 if (x >= 0 && x < m && y >= 0 && y < n && id[x * n + y] != -1) { 19 int root = root(id, x * n + y); // this is the root of the neibor's islands 20 // 发现root不等的情况下,才union, 同时减小count。注意,一定要用当前index作为新的root,这样才能保证相连的岛是同一个root。 21 if (root != index) { 22 id[root] = index; // set the neighbor's island's root to be the new island index 23 count--; 24 } 25 } 26 } 27 res.add(count); 28 } 29 return res; 30 } 31 32 public int root(int[] id, int i) { 33 while (i != id[i]) { 34 id[i] = id[id[i]]; // 优化,为了减小树的高度 35 i = id[i]; 36 } 37 return i; 38 } 39 }