-
Notifications
You must be signed in to change notification settings - Fork 43
/
PacificAtlanticWaterFlow.java
281 lines (242 loc) · 10.5 KB
/
PacificAtlanticWaterFlow.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
package LeetCodeJava.DFS;
import java.util.*;
public class PacificAtlanticWaterFlow {
// V0
// IDEA : DFS (fixed by GPT)
// https://www.youtube.com/watch?v=s-VkcjHqkGI
public List<List<Integer>> pacificAtlantic(int[][] heights) {
if (heights == null || heights.length == 0 || heights[0].length == 0) {
return new ArrayList<>();
}
int l = heights.length;
int w = heights[0].length;
/**
*
* The pacificReachable and atlanticReachable arrays are used to keep track
* of which cells in the matrix can reach the Pacific and Atlantic oceans, respectively.
*
*
* pacificReachable[i][j] will be true if water can flow from cell (i, j) to the Pacific Ocean.
* The Pacific Ocean is on the top and left edges of the matrix.
*
* atlanticReachable[i][j] will be true if water can flow from cell (i, j) to the Atlantic Ocean.
* The Atlantic Ocean is on the bottom and right edges of the matrix.
*
*
* NOTE !!!!
*
* The pacificReachable and atlanticReachable arrays serve a dual purpose:
*
* Tracking Reachability: They track whether each cell can reach the respective ocean.
* Tracking Visited Cells: They also help in tracking whether a cell has already been visited during the depth-first search (DFS) to prevent redundant work and infinite loops.
*
*/
boolean[][] pacificReachable = new boolean[l][w];
boolean[][] atlanticReachable = new boolean[l][w];
// check on x-axis
for (int x = 0; x < w; x++) {
dfs(heights, pacificReachable, 0, x);
dfs(heights, atlanticReachable, l - 1, x);
}
// check on y-axis
for (int y = 0; y < l; y++) {
dfs(heights, pacificReachable, y, 0);
dfs(heights, atlanticReachable, y, w - 1);
}
List<List<Integer>> commonCells = new ArrayList<>();
for (int i = 0; i < l; i++) {
for (int j = 0; j < w; j++) {
if (pacificReachable[i][j] && atlanticReachable[i][j]) {
commonCells.add(Arrays.asList(i, j));
}
}
}
return commonCells;
}
private void dfs(int[][] heights, boolean[][] reachable, int y, int x) {
int l = heights.length;
int w = heights[0].length;
reachable[y][x] = true;
int[][] directions = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
for (int[] dir : directions) {
int newY = y + dir[0];
int newX = x + dir[1];
/**
* NOTE !!! only meet below conditions, then do recursion call
*
* 1. newX, newY still in range
* 2. newX, newY is still not reachable (!reachable[newY][newX])
* 3. heights[newY][newX] >= heights[y][x]
*
*
* NOTE !!!
*
* The condition !reachable[newY][newX] in the dfs function
* ensures that each cell is only processed once
*
* 1. Avoid Infinite Loops
* 2. Efficiency
* 3. Correctness
*
*
* NOTE !!! "inverse" comparison
*
* we use the "inverse" comparison, e.g. heights[newY][newX] >= heights[y][x]
* so we start from "cur point" (heights[y][x]), and compare with "next point" (heights[newY][newX])
* if "next point" is "higher" than "cur point" (e.g. heights[newY][newX] >= heights[y][x])
* -> then means water at "next point" can flow to "cur point"
* -> then we keep track back to next point of then "next point"
* -> repeat ...
*/
if (newY >= 0 && newY < l && newX >= 0 && newX < w && !reachable[newY][newX] && heights[newY][newX] >= heights[y][x]) {
dfs(heights, reachable, newY, newX);
}
}
}
// V1
// IDEA : DFS
// https://leetcode.com/problems/pacific-atlantic-water-flow/editorial/
/** NOTE !!! init directions */
private static final int[][] DIRECTIONS = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
private int numRows;
private int numCols;
private int[][] landHeights;
public List<List<Integer>> pacificAtlantic_1(int[][] matrix) {
// Check if input is empty
if (matrix.length == 0 || matrix[0].length == 0) {
return new ArrayList<>();
}
// Save initial values to parameters
numRows = matrix.length;
numCols = matrix[0].length;
/** NOTE !!! cope matrix, for reference below */
landHeights = matrix;
boolean[][] pacificReachable = new boolean[numRows][numCols];
boolean[][] atlanticReachable = new boolean[numRows][numCols];
/** NOTE !!! only move 1 direction at once, for avoiding double loop */
// Loop through each cell adjacent to the oceans and start a DFS
for (int i = 0; i < numRows; i++) {
dfs(i, 0, pacificReachable);
dfs(i, numCols - 1, atlanticReachable);
}
/** NOTE !!! only move 1 direction at once, for avoiding double loop */
for (int i = 0; i < numCols; i++) {
dfs(0, i, pacificReachable);
dfs(numRows - 1, i, atlanticReachable);
}
// Find all cells that can reach both oceans
List<List<Integer>> commonCells = new ArrayList<>();
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
if (pacificReachable[i][j] && atlanticReachable[i][j]) {
//commonCells.add(List.of(i, j));
// List<Integer> coord = new ArrayList<>();
// coord.add(i);
// coord.add(j);
// commonCells.add(coord);
commonCells.add(Arrays.asList(i, j));
}
}
}
return commonCells;
}
private void dfs(int row, int col, boolean[][] reachable) {
/** NOTE !!! set visited as true */
// This cell is reachable, so mark it
reachable[row][col] = true;
for (int[] dir : DIRECTIONS) { // Check all 4 directions
int newRow = row + dir[0];
int newCol = col + dir[1];
// Check if new cell is within bounds
if (newRow < 0 || newRow >= numRows || newCol < 0 || newCol >= numCols) {
/** NOTE !!! neglect below code and jump to next loop (continue) */
continue;
}
// Check that the new cell hasn't already been visited
if (reachable[newRow][newCol]) {
/** NOTE !!! neglect below code and jump to next loop (continue) */
continue;
}
// Check that the new cell has a higher or equal height,
// So that water can flow from the new cell to the old cell
if (landHeights[newRow][newCol] < landHeights[row][col]) {
/** NOTE !!! neglect below code and jump to next loop (continue) */
continue;
}
/** NOTE !!! if can reach here, means this move is "OK" then we do next move via recursion call */
// If we've gotten this far, that means the new cell is reachable
dfs(newRow, newCol, reachable);
}
}
// V2
// IDEA : BFS
// https://leetcode.com/problems/pacific-atlantic-water-flow/editorial/
private static final int[][] DIRECTIONS_ = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
private int numRows_;
private int numCols_;
private int[][] landHeights_;
public List<List<Integer>> pacificAtlantic_2(int[][] matrix) {
// Check if input is empty
if (matrix.length == 0 || matrix[0].length == 0) {
return new ArrayList<>();
}
// Save initial values to parameters
numRows_ = matrix.length;
numCols_ = matrix[0].length;
landHeights_ = matrix;
// Setup each queue with cells adjacent to their respective ocean
Queue<int[]> pacificQueue = new LinkedList<>();
Queue<int[]> atlanticQueue = new LinkedList<>();
for (int i = 0; i < numRows_; i++) {
pacificQueue.offer(new int[]{i, 0});
atlanticQueue.offer(new int[]{i, numCols_ - 1});
}
for (int i = 0; i < numCols_; i++) {
pacificQueue.offer(new int[]{0, i});
atlanticQueue.offer(new int[]{numRows_ - 1, i});
}
// Perform a BFS for each ocean to find all cells accessible by each ocean
boolean[][] pacificReachable = bfs(pacificQueue);
boolean[][] atlanticReachable = bfs(atlanticQueue);
// Find all cells that can reach both oceans
List<List<Integer>> commonCells = new ArrayList<>();
for (int i = 0; i < numRows_; i++) {
for (int j = 0; j < numCols_; j++) {
if (pacificReachable[i][j] && atlanticReachable[i][j]) {
// TODO : fix below
//commonCells.add(List.of(i, j));
commonCells.add(Arrays.asList(i, j));
}
}
}
return commonCells;
}
private boolean[][] bfs(Queue<int[]> queue) {
boolean[][] reachable = new boolean[numRows_][numCols_];
while (!queue.isEmpty()) {
int[] cell = queue.poll();
// This cell is reachable, so mark it
reachable[cell[0]][cell[1]] = true;
for (int[] dir : DIRECTIONS_) { // Check all 4 directions
int newRow = cell[0] + dir[0];
int newCol = cell[1] + dir[1];
// Check if new cell is within bounds
if (newRow < 0 || newRow >= numRows_ || newCol < 0 || newCol >= numCols_) {
continue;
}
// Check that the new cell hasn't already been visited
if (reachable[newRow][newCol]) {
continue;
}
// Check that the new cell has a higher or equal height,
// So that water can flow from the new cell to the old cell
if (landHeights_[newRow][newCol] < landHeights_[cell[0]][cell[1]]) {
continue;
}
// If we've gotten this far, that means the new cell is reachable
queue.offer(new int[]{newRow, newCol});
}
}
return reachable;
}
}