|
1 | 1 | # [1254. Number of Closed Islands](https://leetcode.com/problems/number-of-closed-islands/) |
2 | 2 |
|
3 | 3 | ## Traversal From Islands |
4 | | -We can traverse all the islands, and check if current island is closed. We can use DFS or BFS to traverse the grid. |
| 4 | +We can traverse all the islands, and check if current island is closed. We can use DFS or BFS to traverse the grid. Remember! We have to traverse all the cells of the island to check if it's closed even if we found a cell that is not closed. |
5 | 5 |
|
6 | 6 | ```kotlin |
7 | | -class Solution { |
8 | | - private val directions = arrayOf( |
9 | | - intArrayOf(-1, 0), |
10 | | - intArrayOf(1, 0), |
11 | | - intArrayOf(0, -1), |
12 | | - intArrayOf(0, 1) |
13 | | - ) |
14 | | - |
15 | | - private val visited = 2 |
16 | | - fun closedIsland(grid: Array<IntArray>): Int { |
17 | | - val m = grid.size |
18 | | - val n = grid[0].size |
19 | | - var count = 0 |
20 | | - for (i in 0 until m) { |
21 | | - for (j in 0 until n) { |
22 | | - if (grid[i][j] == 0) { |
23 | | - if (dfs(grid, i, j)) count++ |
24 | | - } |
| 7 | +fun closedIsland(grid: Array<IntArray>): Int { |
| 8 | + val m = grid.size |
| 9 | + val n = grid[0].size |
| 10 | + val visited = HashSet<Pair<Int, Int>>() |
| 11 | + var count = 0 |
| 12 | + for (i in 0 until m) { |
| 13 | + for (j in 0 until n) { |
| 14 | + if (grid[i][j] == 0 && visited.contains(i to j).not()) { |
| 15 | + if (dfs(grid, i, j, visited)) count++ |
| 16 | + // if (bfs(grid, i, j, visited)) count++ |
25 | 17 | } |
26 | 18 | } |
27 | | - return count |
28 | 19 | } |
| 20 | + return count |
| 21 | +} |
29 | 22 |
|
30 | | - private fun dfs(grid: Array<IntArray>, x: Int, y: Int): Boolean { |
31 | | - val m = grid.size |
32 | | - val n = grid[0].size |
33 | | - if (x !in 0 until m || y !in 0 until n) return false |
34 | | - if (grid[x][y] == 1) return true |
35 | | - if (grid[x][y] == visited) return true |
| 23 | +private fun dfs(grid: Array<IntArray>, x: Int, y: Int, visited: HashSet<Pair<Int, Int>>): Boolean { |
| 24 | + val m = grid.size |
| 25 | + val n = grid[0].size |
| 26 | + visited.add(x to y) |
| 27 | + var result = true |
| 28 | + for (d in directions) { |
| 29 | + val newX = x + d[0] |
| 30 | + val newY = y + d[1] |
| 31 | + if (newX !in 0 until m || newY !in 0 until n) result = false // Out of boundary, it affects the result. |
| 32 | + else if (visited.contains(newX to newY)) continue // Skip, don't change the result |
| 33 | + else if (grid[newX][newY] == 1) continue // We initialize the result as true, we keep it true (won't change it) untili we reach the boundary. |
| 34 | + else result = dfs(grid, newX, newY, visited) && result |
| 35 | + } |
| 36 | + return result |
| 37 | +} |
36 | 38 |
|
37 | | - grid[x][y] = visited |
| 39 | +private fun dfs(grid: Array<IntArray>, x: Int, y: Int, visited: HashSet<Pair<Int, Int>>): Boolean { |
| 40 | + val m = grid.size |
| 41 | + val n = grid[0].size |
| 42 | + if (x !in 0 until m || y !in 0 until n) return false |
| 43 | + if (visited.contains(x to y)) return true |
| 44 | + if (grid[x][y] == 1) return true |
38 | 45 |
|
39 | | - var result = true |
40 | | - directions.forEach { d -> |
41 | | - // result = result & dfs(grid, x + d[0], y + d[1]) will not work, we have to traverse all the cells and then return the result. |
42 | | - // but we use `&&` to short-circuit the evaluation, it won't traverse all the cells of the same island. |
43 | | - result = dfs(grid, x + d[0], y + d[1]) && result |
44 | | - } |
45 | | - return result |
| 46 | + visited.add(x to y) |
| 47 | + var result = true |
| 48 | + for (d in directions) { |
| 49 | + val newX = x + d[0] |
| 50 | + val newY = y + d[1] |
| 51 | + // result = result & dfs(grid, x + d[0], y + d[1]) will not work, we have to traverse all the cells and then return the result. |
| 52 | + // but we use `&&` to short-circuit the evaluation, it won't traverse all the cells of the same island. |
| 53 | + result = dfs(grid, newX, newY, visited) && result |
46 | 54 | } |
| 55 | + return result |
| 56 | +} |
47 | 57 |
|
48 | | - private fun bfs(grid: Array<IntArray>, i: Int, j: Int): Boolean { |
49 | | - val m = grid.size |
50 | | - val n = grid[0].size |
51 | | - val q = ArrayDeque<Pair<Int, Int>>() |
52 | | - q.addLast(i to j) |
53 | | - var closed = true |
54 | | - while (q.isNotEmpty()) { |
55 | | - val pair = q.removeFirst() |
56 | | - val x = pair.first |
57 | | - val y = pair.second |
58 | | - |
59 | | - if (x !in 0 until m || y!in 0 until n) { |
60 | | - closed = false |
61 | | - continue |
| 58 | +private fun bfs(grid: Array<IntArray>, sourceX: Int, sourceY: Int, visited: HashSet<Pair<Int, Int>>): Boolean { |
| 59 | + val m = grid.size |
| 60 | + val n = grid[0].size |
| 61 | + val queue = ArrayDeque<Pair<Int, Int>>() |
| 62 | + queue.addLast(sourceX to sourceY) |
| 63 | + visited.add(sourceX to sourceY) |
| 64 | + var result = true |
| 65 | + while (queue.isNotEmpty()) { |
| 66 | + val (x, y) = queue.removeFirst() |
| 67 | + for (d in directions) { |
| 68 | + val newX = x + d[0] |
| 69 | + val newY = y + d[1] |
| 70 | + if (newX !in 0 until m || newY !in 0 until n) result = false |
| 71 | + else if (visited.contains(newX to newY)) continue |
| 72 | + else if (grid[newX][newY] == 1) continue |
| 73 | + else { |
| 74 | + queue.addLast(newX to newY) |
| 75 | + visited.add(newX to newY) |
62 | 76 | } |
63 | | - if (grid[x][y] != 0) continue |
64 | | - grid[x][y] = visited |
65 | | - directions.forEach { d -> |
66 | | - q.addLast(x + d[0] to y + d[1]) |
| 77 | + } |
| 78 | + } |
| 79 | + return result |
| 80 | +} |
| 81 | + |
| 82 | +private fun bfs(grid: Array<IntArray>, sourceX: Int, sourceY: Int, visited: HashSet<Pair<Int, Int>>): Boolean { |
| 83 | + val m = grid.size |
| 84 | + val n = grid[0].size |
| 85 | + val queue = ArrayDeque<Pair<Int, Int>>() |
| 86 | + queue.addLast(sourceX to sourceY) |
| 87 | + var result = true |
| 88 | + while (queue.isNotEmpty()) { |
| 89 | + val (x, y) = queue.removeFirst() |
| 90 | + if (x !in 0 until m || y !in 0 until n) result = false |
| 91 | + else if (visited.contains(x to y)) continue |
| 92 | + else if (grid[x][y] == 1) continue |
| 93 | + else { |
| 94 | + visited.add(x to y) |
| 95 | + for (d in directions) { |
| 96 | + val newX = x + d[0] |
| 97 | + val newY = y + d[1] |
| 98 | + queue.addLast(newX to newY) |
67 | 99 | } |
68 | 100 | } |
69 | | - return closed |
70 | 101 | } |
| 102 | + return result |
71 | 103 | } |
72 | 104 | ``` |
73 | 105 |
|
74 | 106 | ## Traversal From Boundary |
75 | | -To find the closed island, we can find non-closed islands first, then find the closed islands. How can we find the non-closed islands? We can start searching from the edges of the grid, the islands which are connected to the edges are non-closed islands. We can mark them as invalid during traversal. Then we can find the closed islands by traversing the grid again. |
| 107 | +To find the closed island, we can find open islands first, then find the closed islands. We can start searching from the edges of the grid, the islands which are connected to the edges are non-closed islands. We can mark them as invalid during traversal. Then we can find the closed islands by traversing the grid again. |
76 | 108 |
|
77 | 109 | * We traversal (either DFS or BFS) from the 4 edges to search **non-closed** islands and mark the region as invalid during traversal. |
78 | 110 | * Then we traversal the graph again to find `0`, that would be closed islands. |
79 | 111 |
|
80 | 112 | ```kotlin |
81 | | -class Solution { |
82 | | - private val invalid = 2 |
83 | | - private val directions = arrayOf( |
84 | | - -1 to 0, 1 to 0, 0 to -1, 0 to 1) |
| 113 | +private val invalid = 2 |
| 114 | + |
| 115 | +fun closedIsland(grid: Array<IntArray>): Int { |
| 116 | + val m = grid.size |
| 117 | + val n = grid[0].size |
85 | 118 |
|
86 | | - fun closedIsland(grid: Array<IntArray>): Int { |
87 | | - val m = grid.size |
88 | | - val n = grid[0].size |
89 | | - |
90 | | - for (row in 0 until m) { |
91 | | - dfs(grid, row, 0) |
92 | | - dfs(grid, row, n - 1) |
93 | | - } |
94 | | - |
95 | | - for (col in 0 until n) { |
96 | | - dfs(grid, 0, col) |
97 | | - dfs(grid, m - 1, col) |
98 | | - } |
99 | | - |
100 | | - var count = 0 |
101 | | - for (row in 0 until m) { |
102 | | - for (col in 0 until n) { |
103 | | - if (grid[row][col] == 0) { |
104 | | - dfs(grid, row, col) |
105 | | - count++ |
106 | | - } |
107 | | - } |
108 | | - } |
109 | | - return count |
| 119 | + for (row in 0 until m) { |
| 120 | + dfs(grid, row, 0) |
| 121 | + dfs(grid, row, n - 1) |
| 122 | + } |
| 123 | + |
| 124 | + for (col in 0 until n) { |
| 125 | + dfs(grid, 0, col) |
| 126 | + dfs(grid, m - 1, col) |
110 | 127 | } |
111 | 128 |
|
112 | | - private fun dfs(grid: Array<IntArray>, x: Int, y: Int) { |
113 | | - if (x in 0 until grid.size && |
114 | | - y in 0 until grid[0].size && |
115 | | - grid[x][y] == 0) { |
116 | | - grid[x][y] = invalid |
117 | | - |
118 | | - directions.forEach { |
119 | | - dfs(grid, x + it.first, y + it.second) |
| 129 | + var count = 0 |
| 130 | + for (row in 0 until m) { |
| 131 | + for (col in 0 until n) { |
| 132 | + if (grid[row][col] == 0) { |
| 133 | + dfs(grid, row, col) |
| 134 | + count++ |
120 | 135 | } |
121 | 136 | } |
122 | 137 | } |
| 138 | + return count |
| 139 | +} |
| 140 | + |
| 141 | +private fun dfs(grid: Array<IntArray>, x: Int, y: Int) { |
| 142 | + val m = grid.size |
| 143 | + val n = grid[0].size |
| 144 | + if (x !in 0 until m || y !in 0 until n) return |
| 145 | + if (visited.contains(x to y)) return |
| 146 | + if (grid[x][y] == 1) return |
| 147 | + |
| 148 | + grid[x][y] = invalid |
| 149 | + for (d in directions) { |
| 150 | + val newX = x + d[0] |
| 151 | + val newY = y + d[1] |
| 152 | + dfs(grid, newX, newY) |
| 153 | + } |
123 | 154 | } |
124 | 155 | ``` |
125 | 156 |
|
|
0 commit comments