Problem
You are given a two-dimensional array (matrix) of potentially unequal height and width containing only 0s and 1s. Each 0 represents land, and each 1 represents part of a river. A river consists of any number of 1s that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1s forming a river determine its size. Write a function that returns an array of the sizes of all rivers represented in the input matrix. Note that these sizes do not need to be in any particular order.
Input
[
[1,0,0,1,0],
[1,0,1,0,0],
[0,0,1,0,1],
[1,0,1,0,1],
[1,0,1,1,0],
]
Output [1,2,2,2,5]
My Approach
After evaluating the problem i felt like this should be done using a graph traversal like algorithm maybe depth first search. So that is exactly what i do .
I traverse the matrix from top left and see if the value is visited and if it is not then and if the value is 1 then i traverse all it's nodes and keep a counter to keep size of the river. In the end i update an array list with the total river size.
For some reason my result is not correct and i am not sure what i did wrong. I hand traced my code too but can't figure out the issue.
My Code
import java.util.ArrayList;
class Program {
public static ArrayList<Integer> riverSizes(int[][] matrix) {
ArrayList<Integer> result = new ArrayList<Integer>();
boolean [][] visitStatus = new boolean [matrix.length][matrix[0].length];
for(int row = 0; row<matrix.length; row++){
for(int col = 0; col<matrix.length; col++){
int count = 0;
count = traverseMatrix(row,col,matrix,visitStatus,count);
result.add(count);
}
}
return result;
}
public static int traverseMatrix(int row, int col, int [][] matrix, boolean [][] visitStatus, int count){
if(visitStatus[row][col] == true){
return count;
}else if(matrix[row][col] == 0){
visitStatus[row][col] = true;
return count;
}else{
count++;
visitStatus[row][col] = true;
if(isValid(row,col-1,matrix)){
return traverseMatrix(row,col-1,matrix,visitStatus,count);
}
if(isValid(row,col+1,matrix)){
return traverseMatrix(row,col+1,matrix,visitStatus,count);
}
if(isValid(row-1,col,matrix)){
return traverseMatrix(row-1,col,matrix,visitStatus,count);
}
if(isValid(row+1,col,matrix)){
return traverseMatrix(row+1,col,matrix,visitStatus,count);
}
}
return count;
}
public static boolean isValid(int row, int col,int[][] matrix){
return (row >= 0 && row < matrix.length) && (col >= 0 && col < matrix[0].length);
}
}
you are given a two-dimensional array (matrix) of potentially unequal height and widthÂ
But you are doing the operation for always same size of matrix both in height and width
for(int row = 0; row<matrix.length; row++){
for(int col = 0; col<matrix.length; col++){ .. }}
you should use the dimension like following way, rest of the things are enough well I guess
..
for(int row = 0; row<matrix.length; row++){
for(int col = 0; col<matrix[row].length; col++){ .. }}
and the changes need to apply in function 'isValid' also
public static boolean isValid(int row, int col,int[][] matrix){
return (row >= 0 && row < matrix.length) && (col >= 0 && col < matrix[row].length);
}
Convert count to a local variable and accumulate it:
static int traverseMatrix(int row, int col, int[][] matrix, boolean[][] visitStatus) {
if (visitStatus[row][col] || matrix[row][col] == 0) {
return 0;
}
visitStatus[row][col] = true;
int count = 1;
if (isValid(row, col - 1, matrix)) {
count += traverseMatrix(row, col - 1, matrix, visitStatus);
}
...
return count;
}
In addition to #OleksandrPyrohov's answer, also check if the current cell is visited already before calling traverseMatrix :
public static ArrayList<Integer> riverSizes(int[][] matrix) {
ArrayList<Integer> result = new ArrayList<Integer>();
boolean [][] visitStatus = new boolean [matrix.length][matrix[0].length];
for(int row = 0; row<matrix.length; row++){
for(int col = 0; col<matrix.length; col++){
if ( !visitStatus[row][col] ) {
int count = 0;
count = traverseMatrix(row,col,matrix,visitStatus,count);
result.add(count);
}
}
}
return result;
}
My solution is written in C#, but it's similar to Java:
You can replace List with ArrayList
Code:
public static List<int> RiverSizes(int[,] matrix)
{
var sizes = new List<int>();
bool[,] visited = new bool[matrix.GetLength(0), matrix.GetLength(1)];
for (int row = 0; row < matrix.GetLength(0); row++)
for (int col = 0; col < matrix.GetLength(1); col++)
if (visited[row, col])
continue;
else
Traverse(matrix, row, col, visited, sizes);
return sizes;
}
public static void Traverse(int[,] matrix, int row, int col, bool[,] visited, List<int> sizes)
{
int currentSize = 0;
var toExplore = new List<int[]>
{
new int[] { row, col }
};
while (toExplore.Count > 0)
{
var current = toExplore[^1];
toExplore.RemoveAt(toExplore.Count - 1);
row = current[0];
col = current[1];
if (visited[row, col])
continue;
visited[row, col] = true;
if (matrix[row, col] == 0)
continue;
currentSize++;
foreach (int[] item in GetNeighbours(matrix, row, col, visited))
toExplore.Add(item);
}
if (currentSize > 0)
sizes.Add(currentSize);
}
public static List<int[]> GetNeighbours(int[,] matrix, int row, int col, bool[,] visited)
{
List<int[]> neighbours = new List<int[]>();
if (row > 0 && !visited[row - 1, col])
neighbours.Add(new int[] { row - 1, col });
if (row < matrix.GetLength(0) - 1 && !visited[row + 1, col])
neighbours.Add(new int[] { row + 1, col });
if (col > 0 && !visited[row, col - 1])
neighbours.Add(new int[] { row, col - 1 });
if (col < matrix.GetLength(1) - 1 && !visited[row, col + 1])
neighbours.Add(new int[] { row, col + 1 });
return neighbours;
}
I hope it helps ^-^
Related
I have little problem with removing col from a 2d array.
The goal is to remove from every row specific index "2" and give it back.
Its how it need to be
I made it but got little problem with 0 at the end.
Its how I got
private static void removeEntry(int[][] workArray, int col) {
int row = workArray.length;
//largest row count
int max = 0;
int tempNum = 0;
for (int[] ints : workArray) {
tempNum = 0;
for (int j = 0; j < ints.length; j++) {
tempNum++;
if (tempNum > max) {
max = tempNum;
}
}
}
int [][] newArray = new int[row][max];
for(int i = 0; i < row; i++) {
for(int j = 0; j < max; j++) {
if(j < col && j < workArray[i].length) {
newArray[i][j] = workArray[i][j];
}else if (j == col) {
// Do nothing
} else if (j > col && j < workArray[i].length) {
newArray[i][j - 1] = workArray[i][j];
}
}
}
for (int i = 0; i < workArray.length; i++) {
for (int j = 0; j < workArray[i].length; j++) {
workArray[i][j] = newArray[i][j];
}
}
Then I tried to remove 0 but don't work
int remIndex = 0;
for (int i = 0; i < workArray.length; i++) {
for (int j = remIndex; j < workArray[i].length-1; j++) {
if(workArray[i][j] == remIndex){
workArray[i][j] = workArray[i][j + 1];
}
}
}
An array is a container of data that occupies a contiguous block of memory, its size should be defined when the array is being instantiated and can't be changed.
If you need an array of smaller or greater length, then you need to create a new one and copy all previously added elements that should be retained.
There's also no such thing in Java as "2D" arrays, we can create a nested array, i.e. an array containing other arrays.
And similarly to how we can reassign an integer value at a particular index in a plain array int[], we can change a reference in the array of arrays, i.e. we can make it point to another (newly created) array.
And that's what is required according to do in your assignment since the method is void. I.e. the given array needs to be changed by replacing all of its "rows", that greater or equal in length than the given column to remove col, with a new array that will retain all the previous element except for the one at index col.
private static void removeEntry(int[][] workArray, int col) {
for (int row = 0; row < workArray.length; row++) {
if (workArray[row].length <= col) { // no need to change anything
continue;
}
int newLength = workArray[row].length - 1;
int[] newRow = new int[newLength]; // creating a new row shorter by 1
for (int oldCol = 0, newCol = 0; oldCol < workArray[row].length; oldCol++, newCol++) {
if (oldCol == col) { // skipping the target column
newCol--; // newCol would be incremented automatically at the end of the iteration, but we want it to remain the same
continue;
}
newRow[newCol] = workArray[row][oldCol];
}
workArray[row] = newRow; // reassigning the row
}
}
main()
public static void main(String[] args) {
int[][] testArr =
{{1, 2},
{1, 2, 3},
{1, 2, 3, 4}};
removeEntry(testArr, 2);
// printing the testArr
for (int[] arr: testArr) {
System.out.println(Arrays.toString(arr));
}
}
Output:
[1, 2]
[1, 2]
[1, 2, 4]
A link to the Online Demo
If you've made the method to be void by mistake, you are required to return a new array, i.e. the return type int[] (check the requirements of your assignment carefully). Then a minor change needs to be applied to the logic explained and implemented above: every array should be replaced with a new one and then placed into the newly created resulting array.
Note Arrays.copyOf() allows creating a duplicate of the hole array, and System.arraycopy() can help you to copy the elements in the given range from one array into another, but because you are working on an assignment I suggest you to do it manually with loops because you are expected to demonstrate the knowledge on how to manipulate with arrays, not the knowledge of special utility features (unless otherwise specified in the assignment).
private static int[][] removeEntry(int[][] workArray, int col) {
int[][] result = new int[workArray.length][];
for (int row = 0; row < workArray.length; row++) {
int newLength = col < workArray[row].length ? workArray[row].length - 1 : workArray[row].length;
int[] newRow = new int[newLength];
for (int oldCol = 0, newCol = 0; oldCol < workArray[row].length; oldCol++, newCol++) {
if (oldCol == col) {
newCol--;
continue;
}
newRow[newCol] = workArray[row][oldCol];
}
result[row] = newRow; // reassigning the row
}
return result;
}
main()
public static void main(String[] args) {
int[][] testArr =
{{1, 2},
{1, 2, 3},
{1, 2, 3, 4}};
int[][] newArr = removeEntry(testArr, 2);
// printing the testArr
for (int[] arr: newArr) {
System.out.println(Arrays.toString(arr));
}
}
Output:
[1, 2]
[1, 2]
[1, 2, 4]
A link to the Online Demo
Here is my approach on this. I used System.arraycopy() to copy the array up to the removed column and from directly after the removed column to the end. This way, we remove the column completely from the array.
private static int[][] removeEntry(int[][] workArray, int col) {
int[][] resultArray = new int[workArray.length][];
int index = 0;
for (int[] row : workArray) {
if (row.length - 1 < col) {
resultArray[index] = row;
index++;
continue;
}
int[] arrayCopy = new int[row.length - 1];
System.arraycopy(row, 0, arrayCopy, 0, col);
System.arraycopy(row, col + 1, arrayCopy, col, row.length - col - 1);
resultArray[index] = arrayCopy;
index++;
}
return resultArray;
}
Arrays are of fixed size. At the time of initializing array, a default value is stored in it. Here, 0 is stored as default. So I would suggest you to not initialize "col" as "max" but rather do it in for loop like this :-
int [][] newArray = new int[row][];
for(int i = 0; i < row; i++) {
if(col <= workArray[i].length){
newArray[i] = new int[workArray[i].length-1]; //initialize it here
}
for(int j = 0; j < workArray[i].length; j++) {
if(j < col) {
newArray[i][j] = workArray[i][j];
}else if (j == col) {
// Do nothing
} else if (j > col) {
newArray[i][j - 1] = workArray[i][j];
}
}
}
return newArray;
You are also supposed to return modified array as we can't changed the size of array and thus we had to create a new one ( newArray ). So do change the method definition as :-
private static int[][] removeEntry(int[][] workArray, int col)
Finally, whole method would look like :-
private static int[][] removeEntry(int[][] workArray, int col) {
int row = workArray.length;
//largest row count
int [][] newArray = new int[row][];
for(int i = 0; i < row; i++) {
if(col <= workArray[i].length){
newArray[i] = new int[workArray[i].length-1]; //initialize it here
}
for(int j = 0; j < workArray[i].length; j++) {
if(j < col) {
newArray[i][j] = workArray[i][j];
}else if (j == col) {
// Do nothing
} else if (j > col) {
newArray[i][j - 1] = workArray[i][j];
}
}
}
return newArray;
}
and you can use it like :-
workArray = removeEntry(workArray, 2);
I've just learned an algorithm called DFS and when I watched the videos, the most popular practice or example to use DFS is the Connected Cells or find islands one which is a 2D array with 0 and 1. But I really wish to know if I want the island shape only to be rectangular(or square), what should I do or how can I change the if or for loop in the program. Here's the code I'm looking at:
// No. of rows and columns
static final int ROW = 3, COL = 5;
// A function to check if a given cell (row, col) can
// be included in DFS
boolean isSafe(int M[][], int row, int col,
boolean visited[][])
{
// row number is in range, column number is in range
// and value is 1 and not yet visited
return (row >= 0) && (row < ROW) && (col >= 0) && (col < COL) && (M[row][col] == 1 && !visited[row][col]);
}
// A utility function to do DFS for a 2D boolean matrix.
// It only considers the 8 neighbors as adjacent vertices
void DFS(int M[][], int row, int col, boolean visited[][])
{
// These arrays are used to get row and column numbers
// of 8 neighbors of a given cell
int rowNbr[] = new int[] { -1, -1, -1, 0, 0, 1, 1, 1 };
int colNbr[] = new int[] { -1, 0, 1, -1, 1, -1, 0, 1 };
// Mark this cell as visited
visited[row][col] = true;
// Recur for all connected neighbours
for (int k = 0; k < 8; ++k)
if (isSafe(M, row + rowNbr[k], col + colNbr[k], visited))
DFS(M, row + rowNbr[k], col + colNbr[k], visited);
}
// The main function that returns count of islands in a given
// boolean 2D matrix
int countIslands(int M[][])
{
boolean visited[][] = new boolean[ROW][COL];
int count = 0;
for (int i = 0; i < ROW; ++i)
for (int j = 0; j < COL; ++j)
if (M[i][j] >= 100 && !visited[i][j])
{
DFS(M, i, j, visited);
++count;
}
return count;
}
I am trying to store all of the values in the matrix from the top right to the bottom left and store them in an array.
int matrixSample [][] = {
{6,4,1,4},
{7,5,4,4},
{4,4,8,3},
{4,4,8,3}
};
The output should be
[4,1,4,4,4,3,6,5,8,3,7,4,8,4,4,4]
I can get the bottom right diagonal
static int[] getAllDiagonalsInMatrix(int matrix[][]){
// Sum of arithmetic progression
int diagonal[] = new int[matrix.length * (matrix.length + 1)*2];
int index = 0;
for(int row = 0; row < matrix.length; row++) {
for(int col = 0; col < matrix[row].length - row; col++) {
diagonal[index++] = matrix[row + col][col];
}
}
return diagonal;
}
Is this even possible to do using the same two loops by adjustments made in the loops above?
Okay, here is my thought process on your problem. However, I'm going to print values instead of collecting them to make it a little easier on me and keep the solution easy to read.
First, how do you get a diagonal? We need to do this frequently so lets start by making a function for that. Maybe we could pass in the top left corner of the diagonal and go from there.
public void getDiagonal(int[][] array, int row, int col) {
// While row and col are within the bounds of the array
while (row < array.length && col < array[row].length) {
// Print element in diagonal
System.out.println(array[row][col]);
// Diagonal moves from top-left to bottom-right
row++;
col++;
}
}
Now that we have a function to get a diagonal, we just need a way to call it. Essentially, we just need to follow an L shape going from the top-right to the top-left to the bottom-left.
// Get diagonals starting in the first row with a column > 0
for (int col = array.length - 1; col > 0; col--) {
getDiagonal(array, 0, col);
}
// Get all diagonals starting from the left most column
for (int row = 0; row < array.length; row++) {
getDiagonal(array, row, 0);
}
Now that we have a working way to iterate through the values, we can rewrite it to save the values into an array instead. You could also choose to remove the function entirely now that you have a process.
Edit: I almost forgot, but the mathematical solution you were looking for is as follows.
for (int row = 0; row < array.length; row++) {
for (int col = 0; col < array.length; col++) {
// Index along diagonal
int diagonal = Math.min(row, col);
// Which part of L contains value
if (col >= row) {
int start = array.length - 1 - (col - row);
int passed = start * (start + 1) / 2;
solution[passed + diagonal] = array[row][col];
} else {
int start = array.length - 1 - (row - col);
int passed = array.length * array.length - 1 - start * (start + 1) / 2; solution[passed - array.length + 1 + row] = array[row][col];
}
}
}
One solution is to iterate through a matrix where you consider positions outside of the matrix, but exclude every index out of bounds.
static int[] getDiagonals(int[][] mat) {
int diagonal[] = new int[mat.length * (mat[0].length)];
int index = 0;
int yStart = -mat[0].length;
for (int y = yStart; y < mat.length; y++) {
for (int x = 0; x < mat[0].length; x++) {
if (y + x >= 0 && y + x < mat.length) {
diagonal[index++] = mat[y+x][x];
}
}
}
return diagonal;
}
Might not be optimal as you are effectively traversing a matrix nearly twice the size, but it is pretty intuitive.
"Find the maximum element (s) in the matrix and remove from the matrix all the rows and columns containing it".
I made the methods. In one, I find the largest number in the matrix. And in the second, I delete from the matrix the row and column that contains the largest number. But it works correctly only if the largest number is the only one. How to make, that deleted all lines and all columns in which the greatest number contains?
private void deleteRowCol() {
int[][] matrix = getMatrix();
int max = matrix[0][0];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (max < matrix[i][j]) {
max = matrix[i][j];
}
}
}
int[] m = findIdMax(matrix, max);
int[][] outMatrix = new int[matrix.length - 1][matrix[0].length - 1];
int r = 0;
for (int i = 0; i < outMatrix.length; i++) {
if (i > m[0] - 1) {
r = 1;
}
int c = 0;
for (int j = 0; j < outMatrix[0].length; j++) {
if (j > m[1] - 1) {
c = 1;
}
outMatrix[i][j] = matrix[i + r][j + c];
}
}
System.out.println(" ");
outputMatrix(outMatrix);
}
private int[] findIdMax(int[][] matrix, int max) {
int[] id = {0, 0};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (max == matrix[i][j]) {
id[0] = i;
id[1] = j;
}
}
}
return id;
}
expected output:
with this matrix
4 2 0 -3
4 -1 4 1
0 2 -4 3
-4 -1 -4 -2
should bring
-2 3
-1 -2
I haven't been able to find a fix for your current code. One issue is that you always assume 1 row and 1 column are deleted with int[][] outMatrix = new int[matrix.length - 1][matrix[0].length - 1];. If I put a loop around your code it would fail if the max is for example at positions 1,2 and 1,4 in the matrix (which should remove only 1 row, but 2 columns).
So perhaps someone else could take a closer look at your implementation and see a straight-forward fix without changing too much. Instead, I've thought about the use case from scratch and tried to complete the task myself. I ended up with the following code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
class Main{
private Set<Integer> rowsToDelete,
columnsToDelete;
public static void main(String[] a){
Main program = new Main();
int[][] matrix = program.getMatrix();
System.out.println("Before:");
program.prettyPrintMatrix(matrix);
System.out.println();
int[][] modifiedMatrix = program.deleteRowCol(matrix);
System.out.println("After:");
program.prettyPrintMatrix(modifiedMatrix);
}
private int[][] getMatrix(){
// Test:
return new int[][]{
{ 4, 2, 0,-3},
{ 4,-1, 4, 1},
{ 0, 2,-4, 3},
{-4,-1,-4,-2}
};
}
private int[][] deleteRowCol(int[][] matrix) {
int max = findMax(matrix);
determineCoordinatesMax(matrix, max);
// Some debug prints:
System.out.println("Maximum: "+max);
System.out.println("Rows to delete: "+rowsToDelete);
System.out.println("Columns to delete: "+columnsToDelete);
System.out.println();
int[][] modifiedMatrix = deleteRows(matrix);
modifiedMatrix = deleteColumns(modifiedMatrix);
return modifiedMatrix;
}
private int findMax(int[][] matrix){
int max = matrix[0][0];
for(int[] row : matrix){
for(int value : row){
if(value > max){
max = value;
}
}
}
return max;
}
private void determineCoordinatesMax(int[][] matrix, int max) {
rowsToDelete = new HashSet<>();
columnsToDelete = new HashSet<>();
for(int r=0; r<matrix.length; r++){
for(int c=0; c<matrix[r].length; c++){
if(matrix[r][c] == max){
rowsToDelete.add(r);
columnsToDelete.add(c);
}
}
}
}
private int[][] deleteRows(int[][] matrix){
int rowsToLeave = matrix.length - rowsToDelete.size();
int[][] modifiedMatrix = new int[rowsToLeave][];
int i = 0;
for(int r=0; r<matrix.length; r++){
if(!rowsToDelete.contains(r)){
modifiedMatrix[i] = matrix[r];
i++;
}
}
return modifiedMatrix;
}
private int[][] deleteColumns(int[][] matrix){
int columnsAlreadyDeleted = 0;
for(int columnToDelete : columnsToDelete){
// Delete the columns one by one:
int[][] modifiedMatrix = new int[matrix.length][matrix[0].length - 1];
for(int r=0; r<matrix.length; r++){
int i=0;
for(int c=0; c<matrix[r].length; c++){
if(c != columnToDelete - columnsAlreadyDeleted){
modifiedMatrix[r][i] = matrix[r][c];
i++;
}
}
}
columnsAlreadyDeleted++;
matrix = modifiedMatrix;
}
return matrix;
}
private void prettyPrintMatrix(int[][] matrix){
for(int[] row : matrix){
System.out.println(Arrays.toString(row));
}
}
}
The deletion of the columns could use some tweaking, since I now have three nested loops (loop over the columns to delete; inner loop over the matrix-rows; inner loop over the matrix-columns). But it works, and removes the rows and columns of the int[][] as expected.
You can see it in action here: Try it online.
int [][]a=new int[9][9] //creates a "square" with 81 0's
How do I go about checking if a input (when filling out the array) already exist in the column or row?
For example, for the row 0, how can I make sure I don't get the number 9 two times (for instance, once on row 0, column 0 and second on row 0 column 1)? I can't seem to figure out how to go about this (all I know is that I'll probably need two for loops? and a if statement. I'm not sure how to go about the if statement tho).
public static void main(String[] args)
{
int[][] a = new int[9][9];
boolean areThereDuplicates;
int input = 9; // or any input of course
int row = 0; // or any row
int column = 0;// or any column
areThereDuplicates = checkDuplicates(input, row, column, a);
if (!areThereDuplicates) // if there are no duplicates, input value
a[row][column] = input;
}
public static boolean checkDuplicates(int numChecked, int row, int column, int[][] arr)
{
// checks row
for (int j = 0; j < arr[row].length; j++)
{
if (arr[row][j] == numChecked)
return true;
}
// checks column
for (int k = 0; k < arr.length; k++)
{
if (arr[k][column] == numChecked)
return true;
}
return false;
}
One important thing I wanted to point out is the for loop that checks the column. arr.length does not return 81; rather it returns 9. arr[row].length also returns how many columns there are in the specified row.
Quick draft, not tested but should work:
a[x][y] = newValue; // e.g. 9
return checkDuplicates(x, y, newValue); // returns true if a duplicate is found
public boolean checkDuplicates(int x, int y, int newValue) {
for (int i=0; i<9; i++) {
if (i!=x && a[i][y]==newValue)
return true;
}
for (int i=0; i<9; i++) {
if (i!=y && a[x][i]==newValue)
return true;
}
return false;
}