I was trying to use heuristics and priority queue to solve the leetcode 1091 Shortest Path in Binary Matrix. However, I cannot pass all the tests. Do you have any idea about the error in my code?
For example, the input is [[0,0,0],[1,1,0],[1,1,0]], the output should be 4. But, my code gets the output of 5. The heuristics I used were the direct distance between the current node to the target node.
class Solution {
public int shortestPathBinaryMatrix(int[][] grid) {
int side_length = grid.length;
// if the s left top corner is 1 then, no path exist and return -1
if(grid[0][0]== 1 || grid[side_length - 1][side_length - 1]== 1)
{
return -1;
}
if(side_length == 1)
{
return 1;
}
// 2D array for 8 directions
int[][] directions = new int[][]{{1,0},{-1,0},{0,1},{0,-1},{-1,-1},{-1,1},{1,-1},{1,1}};
PriorityQueue<Node> pqueue = new PriorityQueue<Node>(10, new Comparator<Node>()
{
public int compare(Node i, Node j) {
if(Double.compare(i.heuristic, j.heuristic) < 0){
return 100;
}
else
{
return -100;
}
}
});
double heuristic = e_distance(0, 0, side_length - 1, side_length - 1);
Node start_point = new Node(0, 0, heuristic);
pqueue.add(start_point);
boolean explored[][] = new boolean[side_length][side_length];
explored[0][0] = true;
int output = 1;
while(!pqueue.isEmpty())
{
Node curr_point = pqueue.poll();
int x = curr_point.x;
int y = curr_point.y;
explored[x][y] = true;
if(x == side_length - 1 && y == side_length - 1)
{
return output;
}
for(int[] successor : directions)
{
int successor_x = x + successor[0];
int successor_y = y + + successor[1];
heuristic = e_distance(successor_x, successor_y, side_length - 1, side_length - 1);
Node successor_point = new Node(successor_x, successor_y, heuristic);
if (pqueue.contains(successor_point))
{
continue;
}
if(successor_x >= 0 && successor_x < side_length && successor_y >= 0
&& successor_y < side_length && grid[successor_x][successor_y] == 0
&& !explored[successor_x][successor_y])
{
if(successor_x == side_length - 1 && successor_y == side_length - 1)
{
return output + 1;
}
pqueue.add(successor_point);
}
else
{
continue;
}
}
output++;
}
return -1;
}
public double e_distance(int x, int y, int target_x, int target_y)
{
return Math.sqrt(Math.abs(target_x - x) * Math.abs(target_x - x) + Math.abs(target_y - y)* Math.abs(target_y - y));
}
}
public class Node{
public int x;
public int y;
public double heuristic;
public Node(int x, int y, double heuristic)
{
this.x = x;
this.y = y;
this.heuristic = heuristic;
}
}
The following is a BFS solution based on your code. It is working although it may need further debugging :
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
List<int[][]>grids = new ArrayList<>();
grids.add( new int[][] {{0,1},{1,0}} );//2
grids.add( new int[][]{{0,0,0},{1,1,0},{1,1,0}} ); //4
grids.add( new int[][] {{1,0,0},{1,1,0},{1,1,0}} );//-1
Solution s = new Solution();
for (int[][]grid : grids) {
System.out.println(s.shortestPathBinaryMatrix(grid));
}
}
}
class Solution {
// 2D array for 8 directions
public static int[][] DIRECTIONS = new int[][]{{1,0},{-1,0},{0,1},{0,-1},{-1,-1},{-1,1},{1,-1},{1,1}};
public int shortestPathBinaryMatrix(int[][] grid) {
int side_length = grid.length;
// if the s left top corner is 1 then, no path exist and return -1
if(grid[0][0]== 1 || grid[side_length - 1][side_length - 1]== 1) return -1;
if(side_length == 1) return 1;
Queue<Node> pqueue = new LinkedList<>();
Node start_point = new Node(0, 0);
pqueue.add(start_point);
boolean explored[][] = new boolean[side_length][side_length];//you can use grid values to mark explored
//int output = 1; use Node.parent to mark the path
while(!pqueue.isEmpty()){
Node curr_point = pqueue.poll();
int x = curr_point.x;
int y = curr_point.y;
explored[x][y] = true;
if(x == side_length - 1 && y == side_length - 1) return pathLength(curr_point);
for(int[] successor : DIRECTIONS) {
int successor_x = x + successor[0];
int successor_y = y + + successor[1];
Node successor_point = new Node(successor_x, successor_y);
if (pqueue.contains(successor_point))
{
continue;
}
if(successor_x >= 0 && successor_x < side_length && successor_y >= 0
&& successor_y < side_length
&& grid[successor_y][successor_x] == 0 //NOT grid[successor_x][successor_y] == 0
&& !explored[successor_x][successor_y])
{
//if(successor_x == side_length - 1 && successor_y == side_length - 1)
// return output + 1;
explored[successor_x][successor_y] = true; //mark as explored
successor_point.setParent(curr_point); //mark as child of current node
pqueue.add(successor_point);
}
else //this else does nothing
{
continue;
}
}
}
return -1;
}
private int pathLength(Node node) {
if(node == null) return 0;
int pathLength = 1;
while (node.getParent() !=null){
node = node.getParent();
pathLength++;
}
return pathLength;
}
}
class Node{
public int x, y;
public double cost;
public Node parent = null;
public Node(int x, int y){
this(x, y, 0);
}
public Node(int x, int y, double cost)
{
this.x = x; this.y = y;
this.cost = cost;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
//todo implement equals and hashCode
}
A somewhat better impelementation of shortestPathBinaryMatrix :
public int shortestPathBinaryMatrix(int[][] grid) {
int side_length = grid.length;
// if the s left top corner is 1 then, no path exist and return -1
if(grid[0][0]== 1 || grid[side_length - 1][side_length - 1]== 1) return -1;
if(side_length == 1) return 1;
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(0, 0));
while(!queue.isEmpty()){
Node curr_point = queue.poll();
int x = curr_point.x; int y = curr_point.y;
if(x == side_length - 1 && y == side_length - 1) return pathLength(curr_point);
grid[y][x] = 1;
for(int[] successor : DIRECTIONS) {
int successor_x = x + successor[0];
int successor_y = y + + successor[1];
if(successor_x >= 0 && successor_x < side_length && successor_y >= 0
&& successor_y < side_length
&& grid[successor_y][successor_x] == 0) {
Node successor_point = new Node(successor_x, successor_y);
if (queue.contains(successor_point)){
continue;
}
grid[successor_y][successor_x] = 1; //mark as explored
successor_point.setParent(curr_point); //mark as child of current node
queue.add(successor_point);
}
}
}
return -1;
}
When different edjges have different cost (weighted graph) you may want to implement Dijkstra algorithm. Dijkstra algorithm is basically an enhanced BFS. This is where cost and PriorityQueue are needed.
shortestPathBinaryMatrix becomes:
//Dijkstra's Algorithm
public int shortestPathBinaryMatrix(int[][] grid) {
int side_length = grid.length;
// if the s left top corner is 1 then, no path exist and return -1
if(grid[0][0]== 1 || grid[side_length - 1][side_length - 1]== 1) return -1;
if(side_length == 1) return 1;
PriorityQueue<Node> queue = new PriorityQueue<>(10,(i,j)-> Double.compare(i.cost, j.cost));
queue.add(new Node(0, 0, 0));
while(!queue.isEmpty()){
Node curr_point = queue.poll();
int x = curr_point.x; int y = curr_point.y;
if(x == side_length - 1 && y == side_length - 1) return pathLength(curr_point);
grid[y][x] = 1;
for(int[] successor : DIRECTIONS) {
int successor_x = x + successor[0];
int successor_y = y + + successor[1];
if(successor_x >= 0 && successor_x < side_length
&& successor_y >= 0 && successor_y < side_length
&& grid[successor_y][successor_x] == 0) {
double cost = curr_point.cost+1;
Node successor_point = new Node(successor_x, successor_y, cost);
if (queue.contains(successor_point)) {
continue;
}
grid[successor_y][successor_x] = 1; //mark as explored
successor_point.setParent(curr_point); //mark as child of current node
queue.add(successor_point);
}
}
}
return -1;
}
A heuristic is needed when you implement A* algorithm. A* algorithm is basically an enhanced Dijkstra algorithm.
To implement it you only need to modify the cost calculation to:
double cost = curr_point.cost+1 + heuristic ; soshortestPathBinaryMatrix becomes:
//A* algorithm
public int shortestPathBinaryMatrix(int[][] grid) {
int side_length = grid.length;
// if the s left top corner is 1 then, no path exist and return -1
if(grid[0][0]== 1 || grid[side_length - 1][side_length - 1]== 1) return -1;
if(side_length == 1) return 1;
PriorityQueue<Node> queue = new PriorityQueue<>(10,(i,j)-> Double.compare(i.cost, j.cost));
queue.add(new Node(0, 0, 0));
while(!queue.isEmpty()){
Node curr_point = queue.poll();
int x = curr_point.x; int y = curr_point.y;
if(x == side_length - 1 && y == side_length - 1) return pathLength(curr_point);
grid[y][x] = 1;
for(int[] successor : DIRECTIONS) {
int successor_x = x + successor[0];
int successor_y = y + + successor[1];
if(successor_x >= 0 && successor_x < side_length
&& successor_y >= 0 && successor_y < side_length
&& grid[successor_y][successor_x] == 0) {
double cost = curr_point.cost+1 + distance(successor_x, successor_y, x, y);
Node successor_point = new Node(successor_x, successor_y, cost);
if (queue.contains(successor_point)) {
continue;
}
grid[successor_y][successor_x] = 1; //mark as explored
successor_point.setParent(curr_point); //mark as child of current node
queue.add(successor_point);
}
}
}
return -1;
}
and distance is defined as:
public double distance(int x, int y, int targetX, int targetY) {
return Math.sqrt(Math.pow(targetX - x,2) + Math.pow(targetY - y,2));
}
Hi so I am currently working on a game of life with javafx canvas. However there seems to be a bug in my algorithm. The still lifes are working however the rest is not, the patterns like the glider aren't moving the way they should. Im using a 2d int array, ALIVE is 1 and DEAD is 0. Here is my algorithm:
private void checkRules() {
int[][] newBoard = board;
int amountOfAliveNeighbours;
for (int y = 0; y < board.length; y++) {
for (int x = 0; x < board[y].length; x++) {
amountOfAliveNeighbours = getAmountOfAliveNeighbours(x, y);
if (board[y][x] == ALIVE) {
if (amountOfAliveNeighbours == 2 || amountOfAliveNeighbours == 3) {
newBoard[y][x] = ALIVE;
}else{
newBoard[y][x] = DEAD;
}
} else if (board[y][x] == DEAD){
if (amountOfAliveNeighbours == 3) {
newBoard[y][x] = ALIVE;
}else{
newBoard[y][x] = DEAD;
}
}
}
}
board = newBoard;
}
private int getAmountOfAliveNeighbours(int x, int y) {
int neighbours = 0;
// top left
if (x - 1 >= 0 && y - 1 >= 0) {
if (board[y - 1][x - 1] == ALIVE)
neighbours++;
}
// top center
if (y - 1 >= 0) {
if (board[y - 1][x] == ALIVE)
neighbours++;
}
// top right
if (x + 1 < board[0].length && y - 1 >= 0) {
if (board[y - 1][x + 1] == ALIVE)
neighbours++;
}
// middle left
if (x - 1 >= 0) {
if (board[y][x - 1] == ALIVE)
neighbours++;
}
// middle right
if (x + 1 < board[0].length) {
if (board[y][x + 1] == ALIVE)
neighbours++;
}
// bottom left
if (x - 1 >= 0 && y + 1 < board.length) {
if (board[y + 1][x - 1] == ALIVE)
neighbours++;
}
// bottom center
if (y + 1 < board.length) {
if (board[y + 1][x] == ALIVE)
neighbours++;
}
// bottom right
if (x + 1 < board[0].length && y + 1 < board.length) {
if (board[y + 1][x + 1] == ALIVE)
neighbours++;
}
return neighbours;
}
Allocate the memory for the temporary board like this:
int[][] newBoard = new int[board.length][board[0].length];
I would suggest to refactor calculation of neighbours:
private int getAmountOfAliveNeighbours(int x, int y) {
int neighbours = 0;
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if ((dx !=0 || dy != 0) && isAlive(x + dx, y + dy)) {
neighbours++;
}
}
}
return neighbours;
}
private boolean isAlive(int x, int y) {
return (x >= 0) && (x < board.length) &&
(y >= 0) && (y < board[0].length) &&
(board[x][y] == ALIVE);
}
I did some research on what causes a stack overflow errors, and I can conclude it is being caused by a recursive function in a program that is supposed to "count the number of islands" in an array. I understand what is causing the issue, but not sure why this is happening, or my main question is what to actually do about it. I found that if I slow down the program by having it repeatedly printing out something to the console, it works, but it takes forever to complete. Is there a way I can keep the program speed without the error, or a better way to solve the problem (search up "number of islands" to find the problem). Also, the array is two dimensional with a size of 1050 by 800.
public class NumOfIslands {
static boolean[][] dotMap = new boolean[1050][800];
static boolean visited[][] = new boolean[1050][800];
static int total = 0;
public static void main(String args[]) {
defineArrays();
run();
}
public static void findObjects(int xCord, int yCord) {
for(int y = yCord - 1; y <= yCord + 1; y++) {
for(int x = xCord - 1; x <= xCord + 1; x++) {
if(x > -1 && y > -1 && x < dotMap[0].length && y < dotMap.length) {
if((x != xCord || y != yCord) && dotMap[x][y] == true && visited[x][y] != true) {
visited[x][y] = true;
findObjects(x,y);
//System.out.println("test");
}
}
}
}
}
public static void defineArrays() {
for(int y = 0; y < 800; y++) {
for(int x = 0; x < 1050; x++) {
dotMap[x][y] = true;
}
}
}
public static int run() {
//dotMap = DisplayImage.isYellow;
System.out.println(dotMap.length + " " + dotMap[0].length);
int objects = 0;
for(int y = 439; y < 560/*dotMap[0].length*/; y++) {
for(int x = 70; x < 300/*dotMap.length*/; x++) {
if(dotMap[x][y] == true && visited[x][y] != true) {
visited[x][y] = true;
objects++;
findObjects(x,y);
}
}
}
System.out.println("total" + total);
System.out.println(objects);
return objects;
}
}
StackOverflowError reasons. In your example each call to findObjects adds 2 variables to the stack int x and int y from loops.
One of the fastest solution:
class Solution {
int m, n;
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
m = grid.length;
n = grid[0].length;
int counter = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
visit(grid, i, j);
counter++;
}
}
}
return counter;
}
public void visit(char[][] grid, int i, int j) {
if (i < 0 || i >= m || j < 0 || j >= n) {
return;
}
if (grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
visit(grid, i - 1, j);
visit(grid, i + 1, j);
visit(grid, i, j - 1);
visit(grid, i, j + 1);
}
}
All recursive algorithms can be implemented with loops. One of the example is below. The Solution implements BFS (Breadth-first search) algorithm, more details on wikipedia.
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
++num_islands;
grid[r][c] = '0'; // mark as visited
Queue<Integer> neighbors = new LinkedList<>();
neighbors.add(r * nc + c);
while (!neighbors.isEmpty()) {
int id = neighbors.remove();
int row = id / nc;
int col = id % nc;
if (row - 1 >= 0 && grid[row-1][col] == '1') {
neighbors.add((row-1) * nc + col);
grid[row-1][col] = '0';
}
if (row + 1 < nr && grid[row+1][col] == '1') {
neighbors.add((row+1) * nc + col);
grid[row+1][col] = '0';
}
if (col - 1 >= 0 && grid[row][col-1] == '1') {
neighbors.add(row * nc + col-1);
grid[row][col-1] = '0';
}
if (col + 1 < nc && grid[row][col+1] == '1') {
neighbors.add(row * nc + col+1);
grid[row][col+1] = '0';
}
}
}
}
}
return num_islands;
}
}
the problem is in this function
public static void findObjects(int xCord, int yCord) {
for(int y = yCord - 1; y <= yCord + 1; y++) {
for(int x = xCord - 1; x <= xCord + 1; x++) {
if(x > -1 && y > -1 && x < dotMap[0].length && y < dotMap.length) {
if((x != xCord || y != yCord) && dotMap[x][y] == true && visited[x][y] != true) {
visited[x][y] = true;
findObjects(x,y);
//System.out.println("test");
}
}
}
}
}`
at here you are builiding a stack of recursive calls to findobjects and ultimately it has no termination condition so it ends up at infinite stacks of findobjects, so my solution is if you are just checking that if x and y varaibles are not equal and visited[x][y] is not true then there is no need to call for recursion just comment the recursive call, because your loop already do what you want the recursive call to do.
public static void findObjects(int xCord, int yCord) {
for(int y = yCord - 1; y <= yCord + 1; y++) {
for(int x = xCord - 1; x <= xCord + 1; x++) {
if(x > -1 && y > -1 && x < dotMap[0].length && y < dotMap.length) {
if((x != xCord || y != yCord) && dotMap[x][y] == true && visited[x][y] != true) {
visited[x][y] = true;
//findObjects(x,y);
//System.out.println("test");
}
}
}
}
}
I've been trying to implement simple program that returns valid move of Reversi / Othello game.
Unfortunately, it doesn't work and I cannot really see why. It returns [2,2] which is certainly not valid move.
//myColor, opponentColor, Reversi move etc. are already defined.
I would be glad if you pointed out flaw in my system.
#Override
public ReversiMove makeNextMove(int[][] board) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (board[y][x] != myColor && board[y][x] != opponentColor) {
int nextX = x + 1;
while (nextX < 7 && board[y][nextX] == this.opponentColor) {
if (board[y][nextX + 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX++;
}
nextX = x - 1;
while (nextX > 0 && board[y][nextX] == this.opponentColor) {
if (board[y][nextX - 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX--;
}
int nextY = y + 1;
while (nextY < 7 && board[nextY][x] == this.opponentColor) {
if (board[nextY + 1][x] == this.myColor) {
return new ReversiMove(y,x);
}
nextY++;
}
nextY = y - 1;
while (nextY > 0 && board[nextY][x] == this.opponentColor) {
if (board[nextY - 1][x] == this.myColor) {
return new ReversiMove(y,x);
}
nextY--;
}
nextX = x + 1;
nextY = y + 1;
while (nextX < 7 && nextY < 7 && board[nextY][nextX] == this.opponentColor) {
if (board[nextY + 1][nextX + 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX++;
nextY++;
}
nextX = x - 1;
nextY = y - 1;
while (nextX > 0 && nextY > 0 && board[nextY][nextX] == this.opponentColor) {
if (board[nextY - 1][nextX - 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX--;
nextY--;
}
nextX = x + 1;
nextY = y - 1;
while (nextX < 7 && nextY > 0 && board[nextY][nextX] == this.opponentColor) {
if (board[nextY - 1][nextX + 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX++;
nextY--;
}
nextX = x - 1;
nextY = y + 1;
while (nextX > 0 && nextY < 7 && board[nextY][nextX] == this.opponentColor) {
if (board[nextY + 1][nextX - 1] == this.myColor) {
return new ReversiMove(y,x);
}
nextX--;
nextY++;
}
}
}
}
return new ReversiMove(-1, -1);
}
}
board[x][y] structure...
sometime refer as:
board[y][nextX] instead of board[nextX][y]
I am trying to create the game of life in java but I have difficulty writing the part that checks the number of neighbours. I understand that the problem is when the program gets to the edges of the grid it won't work because the indexes are greater/smaller than the bounds of the array. So the problem is in my Neighbours(). I am not sure how to fix it, I tried expanding the if statements and I also tried putting the whole set of statements in a while loop. The program seems to be working unless there are live cells at the edges of the grid. Any suggestions on this? Thanks in advance.
import java.io.*;
import java.util.Scanner;
public class LifeGrid
{
public int[][] grid;
public int[][] newgrid;
public int getX()
{
return grid[0].length;
}
public int getY()
{
return grid.length;
}
public int getcurrentgen()
{
return currentgen;
}
public int currentgen=0;
// modify neighbours out of boundary problem.
int Neighbours(int x, int y)
{
int neighbours = 0;
if (grid[y][x-1] == 1)
{ neighbours++; }
if (grid[y][x+1] ==1)
{ neighbours++; }
if (grid[y+1][x-1] ==1)
{ neighbours++; }
if (grid[y+1][x+1] ==1)
{ neighbours++; }
if (grid[y+1][x] ==1)
{ neighbours++; }
if (grid[y-1][x-1] ==1)
{ neighbours++; }
if (grid[y-1][x+1] ==1)
{ neighbours++; }
if (grid[y-1][x] ==1)
{ neighbours++; }
return neighbours;
}
public LifeGrid(int x, int y, String filename)
{
grid = new int [y][x];
newgrid = new int[y][x];
File input = new File(filename);
Scanner sc;
try
{
sc = new Scanner(input);
}
catch (FileNotFoundException e)
{
System.out.println("File error");
return;
}
for ( y=0; y< getY(); y++)
{
String line = sc.nextLine();
for( x = 0; x < getX(); x++)
{
if (line.charAt(x) == '*')
{
grid[y][x] = 1;
}
else
{
grid[y][x] = 0;
}
}
}
}
public void run()
{
show();
while(getcurrentgen() < 3)
{
setup();
grid = newgrid;
currentgen++;
show();
}
}
public void setup()
{
for (int y = 0; y < getY(); y++)
{
for (int x = 0;x < getX();x++)
{
if (grid[y][x]== 1)
{
if (Neighbours(x,y) < 2)
{
newgrid[y][x] = 0;
}
if (Neighbours(x,y) > 3)
{
newgrid[y][x] = 0;
}
if (Neighbours(x,y) == 3 || Neighbours(x,y) == 2)
{
newgrid[y][x] = 1;
}
}
if(grid[y][x]==0)
{
if(Neighbours(x,y) == 3)
{
newgrid[y][x]= 1;
}
}
}
}
}
public void show()
{
for(int y =0; y < getY(); y++)
{
for(int x = 0; x < getX(); x++)
{
System.out.print(grid[y][x]);
}
System.out.println();
}
System.out.println("Current generation: "+getcurrentgen());
}
}
you need to add checks for all your points to make sure they are not on boundary. This means checking for both x and y coordinates:
if (x > 0 && grid[y][x - 1] == 1) {
neighbours++;
}
if (x < grid[y].length - 1 && grid[y][x + 1] == 1) {
neighbours++;
}
if (x > 0 && y < grid.length - 1 && grid[y + 1][x - 1] == 1) {
neighbours++;
}
if (x < grid[y].length - 1 && y < grid.length - 1 && grid[y + 1][x + 1] == 1) {
neighbours++;
}
if (y < grid.length - 1 && grid[y + 1][x] == 1) {
neighbours++;
}
if (x > 0 && y > 0 && grid[y - 1][x - 1] == 1) {
neighbours++;
}
if (y > 0 && x < grid[y].length - 1 && grid[y - 1][x + 1] == 1) {
neighbours++;
}
if (y > 0 && grid[y - 1][x] == 1) {
neighbours++;
}
int Neighbours(int x, int y) is called with x=0 and y=0, right?
How do you then evaluate grid[y-1][x-1]?
Where you have
if (grid[y][x-1] == 1)
You just need to skip if this would go out of bounds:
if (x > 0 && grid[y][x-1] == 1)
And similar for all of the others.