patternjavaModerate
Count the one islands in the matrix
Viewed 0 times
theonecountmatrixislands
Problem
What is an island?
A group of connected 1s forms an island. For example, the below matrix contains 5 islands:
I'm looking for code review, best practices, optimizations etc.
```
public final class CountIslands {
private CountIslands() {}
private static enum Direction {
NW(-1, -1), N(0, -1), NE(-1, 1), E(0, 1), SE(1, 1), S(1, 0), SW(1, -1), W(-1, 0);
int rowDelta;
int colDelta;
Direction(int rowDelta, int colDelta) {
this.rowDelta = rowDelta;
this.colDelta = colDelta;
}
public int getRowDelta() {
return rowDelta;
}
public int getColDelta() {
return colDelta;
}
}
private static boolean isValid(int newRow, int newCol, Direction direction, int[][] m, boolean[][] visited) {
// visited was constructed from matrix so we are sure that checking visitor for row lengh would do no harm.
if (newRow = visited.length) return false;
if (newCol = visited[0].length) return false;
if (visited[newRow][newCol]) return false;
return m[newRow][newCol] == 1;
}
private static void dfs(int row, int col, int[][] m, boolean[][] visited) {
visited[row][col] = true;
for (Direction direction : Direction.values()) {
int newRow = row + direction.getRowDelta();
int newCol = col + direction.getColDelta();
if (isValid(newRow, newCol, direction, m, visited)) {
dfs(newRow, newCol, m, visited);
}
}
}
/**
* Returns the number of 1 islands.
*
* @param m the input matrix
* @return the number of 1 islands.
*/
public static int count(int[][] m) {
final boolean[][] visited = new boolean[m.length][m[0].length];
int count = 0;
for (int row = 0; row < m.length; row++)
A group of connected 1s forms an island. For example, the below matrix contains 5 islands:
{{1, 1, 0, 0, 0},
{0, 1, 0, 0, 1},
{1, 0, 0, 1, 1},
{0, 0, 0, 0, 0},
{1, 0, 1, 0, 1}}I'm looking for code review, best practices, optimizations etc.
```
public final class CountIslands {
private CountIslands() {}
private static enum Direction {
NW(-1, -1), N(0, -1), NE(-1, 1), E(0, 1), SE(1, 1), S(1, 0), SW(1, -1), W(-1, 0);
int rowDelta;
int colDelta;
Direction(int rowDelta, int colDelta) {
this.rowDelta = rowDelta;
this.colDelta = colDelta;
}
public int getRowDelta() {
return rowDelta;
}
public int getColDelta() {
return colDelta;
}
}
private static boolean isValid(int newRow, int newCol, Direction direction, int[][] m, boolean[][] visited) {
// visited was constructed from matrix so we are sure that checking visitor for row lengh would do no harm.
if (newRow = visited.length) return false;
if (newCol = visited[0].length) return false;
if (visited[newRow][newCol]) return false;
return m[newRow][newCol] == 1;
}
private static void dfs(int row, int col, int[][] m, boolean[][] visited) {
visited[row][col] = true;
for (Direction direction : Direction.values()) {
int newRow = row + direction.getRowDelta();
int newCol = col + direction.getColDelta();
if (isValid(newRow, newCol, direction, m, visited)) {
dfs(newRow, newCol, m, visited);
}
}
}
/**
* Returns the number of 1 islands.
*
* @param m the input matrix
* @return the number of 1 islands.
*/
public static int count(int[][] m) {
final boolean[][] visited = new boolean[m.length][m[0].length];
int count = 0;
for (int row = 0; row < m.length; row++)
Solution
So, I looked at the problem spec, expecting to see a 3-word description o the title of the problem, and not much else, followed by 'Looking for optimizations, and confirmation that compelxity is O(n log(n) ).
Fortunately, I was disappointed ;-) Your description is improved over previous questions, and I can actually follow it without having to google-search for references and hints. It would have been improved slightly if you had spelled out carefully that diagonally touching
Then, I did the normal-for-me task of thinking 'How would I solve this', and I thought for a moment, and decided I would:
So, I looked through your code, and, it took me a moment, but I found all of those processes in your solution.
So, you have solved this the same way I would have tackled it. The algorithm is 'good', and all that is then left, is to look at how you have implemented it...
That's a pretty short list of nit-picky things. In all, this is a good program.
There is one suggestion I have for your recursion, and that is that there are multiple 'styles' of recursive methods. If you choose one method, and use it consistently (except for those times the other methods are better), it helps. My suggestion is that you should settle on what I call optimistic recursion.... which is the opposite case of what Wikipedia calls 'short-circuit recursion'. What I am saying, is that 'standard' recursion checks the recursive state, and, if it is valid, it does it's work, and it then calls recursion. The short-circuit system does the state-check of the new state before recursing in to that new state.
By way of example, a standard (what I call optimistic) recursion for this problem would be:
This optimistic approach simplifies the call structure significantly.... (and eliminates the isValid() method).
Food for thought.
Finally, I don't like calling your method
Fortunately, I was disappointed ;-) Your description is improved over previous questions, and I can actually follow it without having to google-search for references and hints. It would have been improved slightly if you had spelled out carefully that diagonally touching
1 values combine to form an island... but this is a nit-pick.Then, I did the normal-for-me task of thinking 'How would I solve this', and I thought for a moment, and decided I would:
- scan the matrix cell-by-cell
- if you encounter a 1, use recursion to follow all it's adjacent 1 values
- mark all visited cells as 'seen'
- consider that process a 'hit' for an island
- scan the remaining unseen cells for the next unseen island. (go to 1).
So, I looked through your code, and, it took me a moment, but I found all of those processes in your solution.
So, you have solved this the same way I would have tackled it. The algorithm is 'good', and all that is then left, is to look at how you have implemented it...
- I like the Enum for the directions. It is a good solution.
- I think your variable names have improved recently. I much prefer seeing
rowandcolinstead ofxandy, etc. It is more descriptive, and it helps.
count()could becountIslands()because that is what is being counted.
That's a pretty short list of nit-picky things. In all, this is a good program.
There is one suggestion I have for your recursion, and that is that there are multiple 'styles' of recursive methods. If you choose one method, and use it consistently (except for those times the other methods are better), it helps. My suggestion is that you should settle on what I call optimistic recursion.... which is the opposite case of what Wikipedia calls 'short-circuit recursion'. What I am saying, is that 'standard' recursion checks the recursive state, and, if it is valid, it does it's work, and it then calls recursion. The short-circuit system does the state-check of the new state before recursing in to that new state.
By way of example, a standard (what I call optimistic) recursion for this problem would be:
private static void dfs(int row, int col, int[][] m, boolean[][] visited) {
// check recursion state... and expect it to have failures.
if (row = m.length || col = m[0].length) {
// invalid state, index-out-of-bounds
return;
}
// OK, row/column are valid... more checks
if (visited[row][col]) {
// already seen this valid position.
return;
}
// mark the position seen
visited[row][col] = true;
// if it's not an island....
if (0 == m[row][col]) {
return;
}
// OK, we are still on the island, let's optimistically search around....
for (Direction direction : Direction.values()) {
dfs(row + direction.getRowDelta(), col + direction.getColDelta(), m, visited);
}
}This optimistic approach simplifies the call structure significantly.... (and eliminates the isValid() method).
Food for thought.
Finally, I don't like calling your method
dfs.... it's not a depth-first-search.... since that applies to a tree. The 'cells' in the 'landscape' are not linked in a tree structure, as one cell could be a neighbour to many other cells. A better name would be scan, or even just search.Code Snippets
private static void dfs(int row, int col, int[][] m, boolean[][] visited) {
// check recursion state... and expect it to have failures.
if (row < 0 || row >= m.length || col < 0 || col >= m[0].length) {
// invalid state, index-out-of-bounds
return;
}
// OK, row/column are valid... more checks
if (visited[row][col]) {
// already seen this valid position.
return;
}
// mark the position seen
visited[row][col] = true;
// if it's not an island....
if (0 == m[row][col]) {
return;
}
// OK, we are still on the island, let's optimistically search around....
for (Direction direction : Direction.values()) {
dfs(row + direction.getRowDelta(), col + direction.getColDelta(), m, visited);
}
}Context
StackExchange Code Review Q#41707, answer score: 11
Revisions (0)
No revisions yet.