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));
}
Related
I need help with this problem, I need an array 3x5 and then when the user selects a position the output will show the min value of the adjacents numbers. Like this:
3 5 6 7 8
6 7 8 2 3
0 9 2 1 1
And the user selects the position 1,1. // Diagonals count it too.
Output: The min value around is 0.
This is the code that I have, the problem is I´m asking if there is a better way than spamming if and elses everywhere.
private static int checkAdjacentField(int p1, int p2, int[][] ae) {
int min = Integer.MAX_VALUE;
if (p1 == 0) {
if (p2 == 0) {
if (ae[p1][p2+1] < min) {
min = ae[p1][p2+1];
} else if (ae[p1+1][p2+1] < min) {
min = ae[p1+1][p2+1];
} else if (ae[p1+1][p2] < min) {
min = ae[p1+1][p2];
}
} else if (p2 == 1) {
if (ae[p1][p2+1] < min){
min = ae[p1][p2+1];
} else if (ae[p1+1][p2+1] < min) {
min = ae[p1+1][p2+1];
} else if (ae[p1+1][p2] < min) {
min = ae[p1+1][p2];
} else if (ae[p1+1][p2-1] < min) {
min = ae[p1+1][p2-1];
} else if (ae[p1][p2-1] < min) {
min = ae[p1][p2-1];
}
}
}
return min;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Random r = new Random();
int [][] ar = new int[3][5];
for (int i = 0; i < ar.length; i++) {
System.out.println();
for (int j = 0; j < 5; j++) {
int rand = r.nextInt(9) + 1;
ar[i][j]=rand;
System.out.printf("%3d",ar[i][j]);
}
}
System.out.println();
System.out.println("Select a position [][]: ");
int pos1 = Integer.parseInt(br.readLine());
int pos2 = Integer.parseInt(br.readLine());
System.out.println("The min value around is " + checkAdjacentField(pos1,pos2,ar));
}
}
In the code the 0,0 and 0,1 works and yes, I could spend time doing the spamming method of if else but I want to know if there is a better way so I can improve. Thanks for helping, any idea or answer is welcome.
I think the best way to do that is using the following algo:
List of all the adjacent positions (regardless of if they are in the array or not)
Filter out the ones that are not in the array
Map the remaining positions to their values in the array
Lookup the smallest one (as we are dealing with integers, you can sort them and take the first one)
this way :
private static int checkAdjacentField(int col, int row, int[][] ae) {
int nbRows = ae.length;
int nbCols = ae[0].length;
// Stream all the 8 positions around your position
Stream<Point> positions = Stream.of(
new Point(col-1, row-1), new Point(col-1, row), new Point(col-1, row+1),
new Point(col, row-1), new Point(col, row+1),
new Point(col+1, row-1), new Point(col+1, row), new Point(col+1, row+1));
return positions
.filter(p -> p.x>=0 && p.y>=0 && p.x<nbCols && p.y<nbRows) // keep those inbound
.mapToInt(p -> ae[p.y][p.x]) // replace positions by their values in the array
.sorted() // sort the values
.findFirst().orElse(-1); // take the first one (smallest)
}
You could even generate the list of points, instead of hard coding them
private static int checkAdjacentField(int col, int row, int[][] ae) {
int nbRows = ae.length;
int nbCols = ae[0].length;
// Stream all the 8 positions around your position
Stream<Point> positions = IntStream.rangeClosed(-1, 1).boxed() // -1, 0, 1
.map(c -> IntStream.rangeClosed(-1, 1).boxed() // -1, 0, 1
.map(r -> new Point(col+c, row+r)))
.flatMap(p -> p) // to a list
.filter(p -> !(p.x == col && p.y==row)); // remove center point
// then same as first example
return positions
.filter(p -> p.x>=0 && p.y>=0 && p.x<nbCols && p.y<nbRows)
.mapToInt(p -> ae[p.y][p.x])
.sorted()
.findFirst().orElse(-1);
}
I prefer hard coding them like in the first example though, it is clearer.
How about this?
private static int checkAdjacentField(int p1, int p2, int[][] ae) {
int[] tmp = new int[8];
int left = (p2-1) % ae[0].length;
int right = (p2+1) % ae[0].length;
int up = (p1-1) % ae.length;
int down = (p1+1) % ae.length;
tmp[0] = ae[up][left];
tmp[1] = ae[up][p2];
tmp[2] = ae[up][right];
tmp[3] = ae[p1][left];
tmp[4] = ae[p1][right];
tmp[5] = ae[down][left];
tmp[6] = ae[down][p2];
tmp[7] = ae[down][right];
List<Integer> tmpList = Arrays.stream(tmp).boxed().collect(Collectors.toList());
return tmpList.stream().mapToInt(i -> i).min().orElse(0);
}
Granted, this doesn't account for edges. If you care about edges, you can use Math.min or Math.max to cover these cases:
private static int checkAdjacentField(int p1, int p2, int[][] ae) {
int[] tmp = new int[8];
int left = (Math.max((p2-1),0)) % ae[0].length;
int right = (Math.min((p2+1), ae[0].length-1)) % ae[0].length;
int up = (Math.max((p1-1),0)) % ae.length;
int down = (Math.min((p1+1), ae.length-1)) % ae.length;
tmp[0] = ae[up][left];
tmp[1] = ae[up][p2];
tmp[2] = ae[up][right];
tmp[3] = ae[p1][left];
tmp[4] = ae[p1][right];
tmp[5] = ae[down][left];
tmp[6] = ae[down][p2];
tmp[7] = ae[down][right];
List<Integer> tmpList = Arrays.stream(tmp).boxed().collect(Collectors.toList());
return tmpList.stream().mapToInt(i -> i).min().orElse(0);
}
At any time, we have 8 possible locations for an adjacent number. N, NW, W, SW, S, SE, E, NE. So, we can add them all to an ArrayList<> to iterate over and check for the minimum number, if we're sure doing so won't get us out of bounds.
This looks verbose, but will handle any case, edge or not, for any size 2d array and works with the code you provided:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;
public class Test {
char[] array;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Random r = new Random();
int [][] ar = new int[3][5];
for (int i = 0; i < ar.length; i++) {
System.out.println();
for (int j = 0; j < 5; j++) {
int rand = r.nextInt(9) + 1;
ar[i][j]=rand;
System.out.printf("%3d",ar[i][j]);
}
}
System.out.println();
System.out.println("Select a position [][]: ");
int pos1 = Integer.parseInt(br.readLine());
int pos2 = Integer.parseInt(br.readLine());
System.out.println("The min value around is " + checkAdjacentField(pos1,pos2,ar));
}
private static int checkAdjacentField(int y, int x, int[][] ae) {
int height = ae.length;
int width = ae[0].length;
int min = Integer.MAX_VALUE;
ArrayList<Integer> adj = new ArrayList<Integer>();
// CHECK NORTH, only out of bounds if we're at the NORTH (y == 0)
if (y != 0) { adj.add(ae[y-1][x]); }
// CHECK NORTHWEST, only out of bounds if we're at the NORTHWEST (y == 0 & x == 0)
if (y != 0 && x != 0) { adj.add(ae[y-1][x-1]); }
// CHECK WEST, only out of bounds if we're at the WEST (x == 0)
if (x != 0) { adj.add(ae[y][x-1]); }
// CHECK SOUTHWEST, only out of bounds if we're at the SOUTHWEST (y == (height-1) & x == 0)
if (y != (height-1) && x != 0) { adj.add(ae[y+1][x-1]); }
// CHECK SOUTH, only out of bounds if we're at the SOUTH (y == (height-1))
if (y != (height-1)) { adj.add(ae[y+1][x]); }
// CHECK SOUTHEAST, only out of bounds if we're at the SOUTHEAST (y == (height-1) & x == (width-1))
if (y != (height-1) && x != (width-1)) { adj.add(ae[y+1][x+1]); }
// CHECK EAST, only out of bounds if we're at the EAST (x == (width-1))
if (x != (width-1)) { adj.add(ae[y][x+1]); }
// CHECK NORTHEAST, only out of bounds if we're at the NORTHEAST (y == 0 & x == (width-1))
if (y != 0 && x != (width-1)) { adj.add(ae[y-1][x+1]); }
// Now, we check our min using our list that contains 3-8 entries
for (Integer num : adj) {
if (num < min) { min = num; }
}
return min;
}
}
You can use a for loop like this to parse all adjacents cells , put them in a list and then you can easly calculate the min of that list.
This way , you can reduce the use of If branchs.
adjRow == 0 && adjCol == 0 ==> Middle Cell
int matrix[][]={{3, 5, 6, 7, 8},
{6, 7, 8, 2, 3} ,
{ 0,9,2,1,1}};
List adjacents = new ArrayList<>(8);
int row= 0 ; int col=1 ;
for (int adjRow = -1; adjRow <= 1; ++adjRow) {
for (int adjCol = -1; adjCol <= 1; ++adjCol) {
if ( (adjRow != 0 || adjCol != 0)
// Avoid IndexOutOfBoundsException
&& ( row + adjRow >= 0 && row + adjRow < 3 )
&& ( col + adjCol >= 0 && col + adjCol < 5 )
){
adjacents.add(matrix[row + adjRow][col + adjCol]);
}
}
}
System.out.println(Collections.min(adjacents));
For (0,1) ==> Min = 3. Hope it helps ^^
Used imports :
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
You can iterate only adjacent values of your matrix. For this you need to determine edges of array and skip your element:
private static int checkAdjacentField(int p1, int p2, int[][] ae) {
int minColIdx = p2 == 0 ? p2 : p2 - 1;
int minRowIdx = p1 == 0 ? p1 : p1 - 1;
int maxRowIdx = p1 == ae.length - 1 ? p1 : p1 + 1;
int result = Integer.MAX_VALUE;
for (int i = minRowIdx; i <= maxRowIdx; i++) {
int[] row = ae[i];
int maxColIdx = p2 == row.length - 1 ? p2 : p2 + 1;
for (int k = minColIdx; k <= maxColIdx; k++) {
if (!(p1 == i && p2 == k) && ae[i][k] < result) {
result = ae[i][k];
}
}
}
return result;
}
Simple Program for 3x5 matrix:
private static int checkAdjacentField(int p1, int p2, int[][] ae) {
int min = Integer.MAX_VALUE;
for (int i = p1 - 1; i <= p1 + 1; i++) {
for (int j = p2 - 1; j <= p2 + 1; j++) {
if ((i < 0 || j < 0) || (i > 2 || j > 4) || (i == p1 && j == p2)) {
continue;
}
if (ae[i][j] < min) {
min = ae[i][j];
}
}
}
return min;
}
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 am doing an assignment which creates a maze and then solves it,
I simulate the maze as an array,
using 1 to represent the wall, 0 -- road, 5 -- start, 9 -- destination
if the road has been used, change its value to 2;
it works well for creating mazes,
In most cases, it can find the path to the destination, but sometimes it still pops error stating "empty stack error", like the picture shows...
[]
I can't find the problem(s), may you help me out?
package MazeDFS;
import java.util.Random;
import java.util.Stack;
public class maze {
public static void main(String[] args) {
//create maze
//and solve it
//define the size as 9*9
int r,c;
r = 9;
c = 9;
int[][] newMaze = mazeGenerator(r,c);
newMaze[1][1] = 5;
mazePrinter(newMaze,r,c);
Stack<Step> path = new Stack<Step>();
Step s0 = new Step(1,1);
path.push(s0);
long startTime = System.nanoTime();
newMaze = solve(newMaze, 1, 1, path);
long endTime = System.nanoTime();
System.out.println("Time length:" + (endTime - startTime));
}
public static int[][] mazeGenerator(int x, int y){
Random randNum = new Random();
/*I simulated the maze as a x*y array
Values of the array represent elements of the maze
0 -- road, 1 -- wall, 5 -- start, 9 -- destination
*/
int[][] maze = new int[x][y];
for(int i = 0; i < x; i++) {
for(int j = 0; j < y; j++) {
//bound of the maze
if(i==0 || j==0 || i==x-1 || j==y-1) {
maze[i][j] = 1;
}
//build walls
if(j%2 == 0) {
maze[i][j] = 1;
}
}
}
//randomly choose a row of every wall and make it a road
for(int i = 1; i < y-1; i++) {
int row = randNum.nextInt(x-2)+1;
maze[row][i] = 0;
}
maze[x-2][y-2] = 9;
return maze;
}
public static void mazePrinter(int[][] maze,int x, int y) {
for(int i=0; i<x; i++) {
for(int j=0; j<y; j++) {
System.out.print(maze[i][j]);
}
System.out.println();
}
}
public static int[][] solve(int[][] newMaze, int x, int y, Stack<Step> path) {
mazePrinter(newMaze, 9,9);
System.out.println();
if(newMaze[x][y] == 9) {
Step s = new Step(x,y);
newMaze[x][y] = 2;
path.push(s);
return newMaze;
}
//going down
int dx = 1;
int dy = 0;
if(newMaze[x+dx][y+dy] == 0 || newMaze[x+dx][y+dy] == 9) {
//if visited mark as 2
if(newMaze[x+dx][y+dy] != 9) {
newMaze[x+dx][y+dy] = 2;
}
Step ss = new Step(x+dx,y+dy);
path.push(ss);
System.out.println("going down");
return solve(newMaze, x+dx, y+dy, path);
}
//going up
dx = -1;
dy = 0;
if(newMaze[x+dx][y+dy] == 0 || newMaze[x+dx][y+dy] == 9) {
//if visited mark as 2
if(newMaze[x+dx][y+dy] != 9) {
newMaze[x+dx][y+dy] = 2;
}
Step ss = new Step(x+dx,y+dy);
path.push(ss);
System.out.println("going up");
return solve(newMaze, x+dx, y+dy, path);
}
//turn right
dx = 0;
dy = 1;
if(newMaze[x+dx][y+dy] == 0 || newMaze[x+dx][y+dy] == 9) {
//if visited mark as 2
if(newMaze[x+dx][y+dy] != 9) {
newMaze[x+dx][y+dy] = 2;
}
Step ss = new Step(x+dx,y+dy);
path.push(ss);
System.out.println("turn right");
return solve(newMaze, x+dx, y+dy, path);
}
//turn left
dx = 0;
dy = -1;
if(newMaze[x+dx][y+dy] == 0 || newMaze[x+dx][y+dy] == 9) {
//if visited mark as 2
if(newMaze[x+dx][y+dy] != 9) {
newMaze[x+dx][y+dy] = 2;
}
Step ss = new Step(x+dx,y+dy);
path.push(ss);
System.out.println("turn left");
return solve(newMaze, x+dx, y+dy, path);
}
//if their is no way to go then turn back to the last step
Step sss = path.pop();
int x1 = sss.getX();
int y1 = sss.getY();
System.out.println("return to:"+"x1:"+x +", y1:"+ y);
return solve(newMaze, x1, y1, path);
}
}
class Step{
private int x;
private int y;
Step(int x, int y){
this.x = x;
this.y = y;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
}
In a recent interview I had, they asked me this question:
Consider a battlefield to be made up of square cells of unit dimensions. A soldier on the battlefield can move from a cell to all(8) of it's neighboring cells. soldier has a gun with him which he can shoot the targets up to any distance along any of the 8 possible directions (north,east,west,south,north-east,north-west,south-east,south-west). also some cells are bulletproof which prevents bullets to pass but soldier can walk over them as if it were a normal cell.he can destroy the target from a bulletproof cell but not from a cell behind it.
Given the position of the target, starting position of a target and position of all the bullet proof cells. you have to tell the position of closest shooting point i.e the cell from which, the soldier can shoot the target and is closest to the starting position of the soldier. if there are more than one such cells, output all of them.
Input specifications :
I) size of the battlefield { integer pair (N,M) : battlefield will be of N*M size )
II) starting position of the soldier {integer pair (i,j)}
III) position of the target {integer pair (x,y) : position of the cell on which target is mounted}
IV) position of all the bullet proof cells { list of integer pair a#b : each element in the list is a position of a bullet proof cell}*
Output specifications :
Sequential list of integer pair i#j (cell) that are closest shoot points and must follow row wise traversal.
Note: if the output list contains four shoot points : (2,1), (1,2), (3,2), (2,4) on a 4x4 battle field.
then the correct output will be {1#2,2#1,2#4,3#2} not {1#2,2#1,3#2,2#4}
Example: Input : {2,2} {2,1} {2,2} {1#1,1#2} Output : 2#1
My Code:
public class Test2 {
public static void main(String[] args) {
int[] size = {4, 4};
int[] startPt = {1, 1};
int[] endPt = {4, 2};
String[] impenetrable = {"2#2", "3#3","3#2"};
Process(size, startPt, endPt, impenetrable);
}
public static void Process(int[] size, int[] startPt, int[] endPt, String[] impenStr) {
//Initialize starting position.
int x = startPt[0] - 1;
int y = startPt[1] - 1;
List<String> output = new ArrayList<String>();
int count = 0;
int[][] direction = {{x, y}, {x, y}, {x, y}, {x, y}, {x, y}, {x, y}, {x, y}, {x, y}};
int[][] board = new int[size[0]][size[1]];
board[startPt[0] - 1][startPt[1] - 1] = 1;
board[endPt[0] - 1][endPt[1] - 1] = 2;
//Impenetrable Points
for (String p : impenStr) {
String[] tmp = p.split("#");
board[Integer.parseInt(tmp[0]) - 1][Integer.parseInt(tmp[1]) - 1] = 99;
}
System.out.println("The Board:");
for (int m = 0; m < board.length; m++) {
for (int n = 0; n < board.length; n++) {
System.out.print(board[m][n] + "\t");
}
System.out.println();
}
while (output.isEmpty()) {
output = Surroundings(direction, endPt[0], endPt[1], board);
//for (int m = 0; m < board.length; m++) {
// for (int n = 0; n < board.length; n++) {
// if(m!=startPt[0] - 1 || n!=startPt[1] - 1){
// }
//}
//}
direction[0][0] = direction[0][0] - 1; // up
direction[0][1] = direction[0][1];
direction[1][0] = direction[1][0] + 1; // Down
direction[1][1] = direction[1][1];
direction[2][0] = direction[2][0]; // Left
direction[2][1] = direction[2][1] - 1;
direction[3][0] = direction[3][0]; // right
direction[3][1] = direction[3][1] + 1;
direction[4][0] = direction[4][0] - 1; // NE
direction[4][1] = direction[4][1] + 1;
direction[5][0] = direction[5][0] + 1; // SW
direction[5][1] = direction[5][1] - 1;
direction[6][0] = direction[6][0] + 1; // SE
direction[6][1] = direction[6][1] + 1;
direction[7][0] = direction[7][0] - 1; // NW
direction[7][1] = direction[7][1] - 1;
count++;
if (!output.isEmpty()) {
break;
}
}
System.out.println(Arrays.toString(output.toArray()));
}
public static List<String> Surroundings(int[][] direction, int targetX, int targetY, int[][] board) {
String string = "";
List<String> output = new ArrayList<String>();
for (int i = 0; i < direction.length; i++) {
try {
if (board[direction[i][0]][direction[i][1]] == 99) {
board[direction[i][0]][direction[i][1]] = 0;
}
} catch (ArrayIndexOutOfBoundsException e) {
}
if (checkDirections(board, direction[i][0], direction[i][1], targetX - 1, targetY - 1) == true) {
string = string + (direction[i][0] + 1) + "#" + (direction[i][1] + 1);
}
try {
if (board[direction[i][0]][direction[i][1]] == 0) {
board[direction[i][0]][direction[i][1]] = 99;
}
} catch (ArrayIndexOutOfBoundsException e) {
}
if (!string.equals("")) {
output.add(string);
string = "";
}
}
return output;
}
public static boolean checkDirections(int[][] battleField, int x, int y, int finalX, int finalY) {
return North(battleField, x, y, finalX, finalY) == true
|| South(battleField, x, y, finalX, finalY) == true
|| West(battleField, x, y, finalX, finalY) == true
|| East(battleField, x, y, finalX, finalY) == true
|| NE(battleField, x, y, finalX, finalY) == true
|| SE(battleField, x, y, finalX, finalY) == true
|| NW(battleField, x, y, finalX, finalY) == true
|| SW(battleField, x, y, finalX, finalY) == true;
}
public static boolean North(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return North(board, x - 1, y, targetX, targetY);
}
}
public static boolean South(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return South(board, x + 1, y, targetX, targetY);
}
}
public static boolean West(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return West(board, x, y + 1, targetX, targetY);
}
}
public static boolean East(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return East(board, x, y - 1, targetX, targetY);
}
}
public static boolean NE(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return NE(board, x - 1, y + 1, targetX, targetY);
}
}
public static boolean SW(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return SW(board, x + 1, y - 1, targetX, targetY);
}
}
public static boolean SE(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return SE(board, x + 1, y + 1, targetX, targetY);
}
}
public static boolean NW(int[][] board, int x, int y, int targetX, int targetY) {
if (x < 0 || x >= board.length || y < 0 || y >= board.length || board[x][y] == 99) {
return false;
} else if (x == targetX && y == targetY) {
return true;
} else {
return NW(board, x - 1, y - 1, targetX, targetY);
}
}}
Where I got stuck:
Consider this image to be the battlefield and the yellow block to be the starting point.
In my code, for each loop, I go on searching through the elements of the outer ring.
I get the correct solution for all the blocks horizontally, vertically and diagonally.(Blue Blocks in the image).
But I am unable to access the red blocks. How do I access them too?
Currently your main method has a fixed number (8) of cells that it is checking at each step. This isn't going to work as each 'step' outwards actually increases the number of cells you should be checking by 8.
Given the number of cells you are checking is pretty small the simplest solution is probably to just check every cell on the board rather than stepping outwards. You can maintain a variable with the minimum distance to the target as you go.
The code would look something like this:
int minDistanceToTarget = Integer.MAX_VALUE;
for each position on the board
if position can hit target
if distance < minDistanceToTarget
minDistanceToTarget = distance
output = ""
if distance == minDistanceToTarget
add position to output
Hopefully you see how that works: it reset the output whenever it finds a closer position that can hit the target and then adds all positions at that distance to the output.
As an aside, I think your code would be a lot neater if you encapsulated your positions in a Position class rather than storing as a second dimension in your array. Something like:
enum Direction {
N, NE, E, SE, S, SW, W, NW;
public int getRowDelta();
public int getColDelta();
}
class Position
private final int row;
private final int col;
public Position(int row, int col);
public int distanceTo(Position other);
public Position move(Direction direction);
}
class Board {
private final int width;
private final int height;
private final Position start;
private final Position target;
private final List<Position> bulletproofCells;
public Board(String input);
public String getOutput();
}
You will find you can remove the duplication of code in methods if you encapsulate logic in classes such as these.
I wrote a pathfinding algorithm for android. It seems to be running very slowly and I can not figure out why. I have asked a similar question before, but I didn't get the answers I was looking for (And I have changed code since then). Here is my path finding class :
public class Pathfinding {
private static Node[][] grid;
private static NodeComparator nodeComparator;
static{
nodeComparator = new NodeComparator();
}
public static class NodeComparator implements Comparator<Node> {
#Override
public int compare(Node node1, Node node2) {
if(node1.F > node2.F){
return 1;
}
else if(node1.F < node2.F){
return -1;
}
else{
return 0;
}
}
}
public static Array<Node> findPath(Node start, Node finish, Node[][] _grid) {
Array<Node> path = new Array<Node>();
Array<Node> openList = new Array<Node>();
Array<Node> closedList = new Array<Node>();
grid = _grid;
if(start == null){
return path;
}
if(finish == null){
return path;
}
Node currentNode = start;
currentNode.G = 0;
currentNode.H = getHeuristic(currentNode, finish);
currentNode.parent = null;
openList.add(currentNode);
System.out.println("PATHFINDING STARTED ||| startPos : " + start.position + " finishPos : " + finish.position);
while (openList.size > 0) {
//Sorts open nodes lowest F value to heighest
openList.sort(nodeComparator);
currentNode = openList.first();
//If path is found, return
if (currentNode == finish) {
System.out.println("PATH FOUND...RETURNING -gat5");
return constructPath(currentNode);
}
openList.removeValue(currentNode, true);
closedList.add(currentNode);
int counter = 0;
for (Node neighbor : getNeighbors(currentNode)) {
if (!closedList.contains(neighbor, true)) { //If neighbor not in closed list
if(neighbor != null) { //If neighbor not wall
if(counter == 4){
counter++;
}
int movementCost = checkMovementCost(counter);
if (openList.contains(neighbor, true)) {
if (currentNode.G + movementCost < neighbor.G) {
neighbor.parent = currentNode;
}
} else {
openList.add(neighbor);
neighbor.parent = currentNode;
neighbor.H = getHeuristic(currentNode, finish);
neighbor.G = neighbor.parent.G + movementCost;
}
counter++;
}
}
}
System.out.println(counter);
}
System.out.println("NO FINAL");
System.out.println("NO PATH FOUND RETURNING...");
path.add(start);
return path;
}
private static int checkMovementCost(int neighbor) {
int movementCost = 0;
switch (neighbor) {
//Diagonal
case 0:
case 2:
case 6:
case 8:
movementCost = 16;
break;
//Not Diagonal
case 1:
case 3:
case 5:
case 7:
movementCost = 10;
break;
}
return movementCost;
}
public static Array<Node> constructPath(Node finish) {
Array<Node> pathNodes = new Array<Node>();
Node currentNode = finish;
pathNodes.add(currentNode);
while (currentNode.parent != null) {
currentNode = currentNode.parent;
pathNodes.add(currentNode);
}
return pathNodes;
}
private static float getHeuristic(Node start, Node finish){
int H = 0;
H += Math.abs(start.position.x - finish.position.x);
H += Math.abs(start.position.y - finish.position.y);
return H;
}
private static Array<Node> getNeighbors(Node node){
Array<Node> neighbors = new Array<Node>();
int x = (int)node.position.x;
int y = (int)node.position.y;
if(x - 1 > 0 && x - 1 < grid.length && y + 1 < grid.length && y + 1 > 0){
neighbors.add(grid[x - 1][y + 1]);
}
else{
neighbors.add(null);
}
if(x > 0 && x < grid.length && y + 1 < grid.length && y + 1 > 0){
neighbors.add(grid[x][y + 1]);
}
else{
neighbors.add(null);
}
if(x + 1 > 0 && x + 1 < grid.length && y + 1 < grid.length && y + 1 > 0){
neighbors.add(grid[x + 1][y + 1]);
}
else{
neighbors.add(null);
}
if(x - 1 > 0 && x - 1 < grid.length && y < grid.length && y > 0){
neighbors.add(grid[x - 1][y]);
}
else{
neighbors.add(null);
}
if(x > 0 && x < grid.length && y < grid.length && y > 0){
neighbors.add(grid[x][y]);
}
else{
neighbors.add(null);
}
if(x + 1 > 0 && x + 1 < grid.length && y < grid.length && y > 0){
neighbors.add(grid[x + 1][y]);
}
else{
neighbors.add(null);
}
if(x - 1 > 0 && x - 1 < grid.length && y - 1 < grid.length && y - 1> 0){
neighbors.add(grid[x - 1][y - 1]);
}
else{
neighbors.add(null);
}
if(x > 0 && x < grid.length && y - 1 < grid.length && y - 1 > 0){
neighbors.add(grid[x][y - 1]);
}
else{
neighbors.add(null);
}
if(x + 1 > 0 && x + 1 < grid.length && y - 1 < grid.length && y - 1 > 0){
neighbors.add(grid[x + 1][y - 1]);
}
else{
neighbors.add(null);
}
return neighbors;
}
}
Thank you so much for your help!
**Some more information : ** When I run this algorithm only once, it works fine. But once I run it 3+ times, it starts lose framerate fast. My grid I am using is 200x200.
If your evaluation of the paths is as simple as you are pointing out, probably the slowness of the algorithm has something do do with the ordering that you do in each iteration of the algorithm.
//Sorts open nodes lowest F value to heighest
openList.sort(nodeComparator);
Just using PriorityQueue to order the nodes to be expanded by the algorithm results in a much more efficient implementation. If you want, take a look to the implementation details of the A* algorithm implemented in the library hipster4j. This works in Android, too.
Hope my answer helps.