Java: 在二维矩阵中打印最短路径的有效方法
Java: Efficient way to print shortest path in 2d Matrix
下面是找出最短路径的代码,我试图只打印最短路径,但我得到了所有可能访问过的路径,我能够在 Python 中做到这一点,但是我在 Java 中很难做到这一点,我想知道在 Java 中是否有更好的方法来做到这一点。
Python解法
def helper(grid):
m,n=len(grid),len(grid[0])
deque=collections.deque([[(0,0)]])
seen=set()
while deque:
arr=deque.popleft()
i,j=arr[-1]
if (i,j)==(m-1,n-1):
return arr
seen.add((i,j))
possible=[(x,y) for x,y in [(i+1,j),(i-1,j),(i,j+1),(i,j-1)] if 0<=x<m and 0<=y<n and grid[x][y]!=1]
for x,y in possible:
if (x,y) not in seen:
deque.append(arr+[(x,y)])
grid=[
[0,0,0,0,0,0,0],
[0,0,1,0,0,1,0],
[0,0,1,0,1,1,0],
[0,0,1,0,1,0,1],
[1,1,1,0,0,0,0]
]
print(helper(grid))
Python 代码将预期输出打印为 (0, 0) -> (0, 1) -> (0, 2) -> (0, 3) -> (1, 3) -> (2, 3) -> (3, 3) -> (4, 3) -> (4, 4) -> (4, 5) -> (4, 6)
Java
import java.util.*;
class Main {
private static int[][] dir = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public static void main(String[] args) {
int[][] grid = {
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 1, 0},
{0, 0, 1, 0, 1, 1, 0},
{0, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 0, 0, 0, 0}
};
int[][] path = shortestPath(grid);
System.out.println("shortestPath " + Arrays.deepToString(path));
}
public static int[][] shortestPath(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
if (grid == null || grid.length == 0 || grid[0][0] == 1 || grid[m - 1][n - 1] == 1) return new int[][]{{-1, -1}};
int[][] path = bfs_with_visited(grid, m, n);
return path;
}
private static int[][] bfs_with_visited(int[][] grid, int m, int n) {
boolean[][] visited = new boolean[m][n];
visited[0][0] = true;
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{0, 0});
List<int[]> result = new ArrayList<>();
int path = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] currentCell = queue.remove();
result.add(new int[]{currentCell[0], currentCell[1]});
if (currentCell[0] == m - 1 && currentCell[1] == n - 1) {
return result.toArray(new int[result.size()][2]);
}
visitNeighbours(grid, m, n, visited, queue, currentCell);
}
path++;
}
return new int[][]{{-1, -1}};
}
private static void visitNeighbours(int[][] grid, int m, int n, boolean[][] visited, Queue<int[]> queue, int[] currentCell) {
for (int k = 0; k < dir.length; k++) {
int nextX = dir[k][0] + currentCell[0];
int nextY = dir[k][1] + currentCell[1];
if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && !visited[nextX][nextY] && grid[nextX][nextY] == 0) {
queue.add(new int[]{nextX, nextY});
visited[nextX][nextY] = true;
}
}
}
}
感谢您的意见,TIA。
在您的 python 程序中,如果您在 arr = deque.popleft()
之后放置打印语句,您将看到以下内容:
[(0, 0)]
[(0, 0), (1, 0)]
[(0, 0), (0, 1)]
[(0, 0), (1, 0), (2, 0)]
[(0, 0), (1, 0), (1, 1)]
[(0, 0), (0, 1), (1, 1)]
[(0, 0), (0, 1), (0, 2)]
[(0, 0), (1, 0), (2, 0), (3, 0)]
[(0, 0), (1, 0), (2, 0), (2, 1)]
[(0, 0), (1, 0), (1, 1), (2, 1)]
[(0, 0), (0, 1), (1, 1), (2, 1)]
[(0, 0), (0, 1), (0, 2), (0, 3)]
[(0, 0), (1, 0), (2, 0), (3, 0), (3, 1)]
[(0, 0), (1, 0), (2, 0), (2, 1), (3, 1)]
[(0, 0), (1, 0), (1, 1), (2, 1), (3, 1)]
[(0, 0), (0, 1), (1, 1), (2, 1), (3, 1)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (1, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 6), (2, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5), (3, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5), (4, 6)]
如果你看到,python 程序正在将一个列表附加到队列中,而不仅仅是一个坐标,这意味着它会记住它经过的路径,或者换句话说,它会一直携带路径。
如果你想在Java中模拟同样的事情,你可以按如下方式进行:
private static int[][] bfs_with_visited(int[][] grid, int m, int n) {
boolean[][] visited = new boolean[m][n];
visited[0][0] = true;
Deque<List<int[]>> queue = new ArrayDeque<>();
List<int[]> result = new ArrayList<>();
result.add(new int[] {0,0});
queue.addFirst(result);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i <size; i++ ) {
result = queue.removeFirst();
int[] curr = result.get(result.size()-1);
if (curr[0] == m-1 && curr[1] == n-1) {
return result.toArray(new int[result.size()][2]);
}
visited[curr[0]][curr[1]] = true;
visitNeighbours(grid, m, n, visited, queue, result, curr);
}
}
return new int[][]{{-1, -1}};
}
private static void visitNeighbours(int[][] grid, int m, int n, boolean[][] visited, Deque<List<int[]>> queue,
List<int[]> listSoFar, int[] currentCell) {
for (int k = 0; k < dir.length; k++) {
int nextX = dir[k][0] + currentCell[0];
int nextY = dir[k][1] + currentCell[1];
if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && !visited[nextX][nextY] && grid[nextX][nextY] == 0) {
List<int[]> l = new ArrayList<>(listSoFar);
l.add(new int[] {nextX, nextY});
queue.add(l);
}
}
}
但我不会建议这样做。而是创建一个 class like
class Node {
int[] prev = null;
int[] curr = null;
public Node(int[] curr, int[] prev) {
this.curr = curr;
this.prev = prev;
}
}
并使用它来填充您的队列。当您找到目标 Node
时,使用 prev
字段跟踪路径,直到到达空 prev
.
下面是找出最短路径的代码,我试图只打印最短路径,但我得到了所有可能访问过的路径,我能够在 Python 中做到这一点,但是我在 Java 中很难做到这一点,我想知道在 Java 中是否有更好的方法来做到这一点。
Python解法
def helper(grid):
m,n=len(grid),len(grid[0])
deque=collections.deque([[(0,0)]])
seen=set()
while deque:
arr=deque.popleft()
i,j=arr[-1]
if (i,j)==(m-1,n-1):
return arr
seen.add((i,j))
possible=[(x,y) for x,y in [(i+1,j),(i-1,j),(i,j+1),(i,j-1)] if 0<=x<m and 0<=y<n and grid[x][y]!=1]
for x,y in possible:
if (x,y) not in seen:
deque.append(arr+[(x,y)])
grid=[
[0,0,0,0,0,0,0],
[0,0,1,0,0,1,0],
[0,0,1,0,1,1,0],
[0,0,1,0,1,0,1],
[1,1,1,0,0,0,0]
]
print(helper(grid))
Python 代码将预期输出打印为 (0, 0) -> (0, 1) -> (0, 2) -> (0, 3) -> (1, 3) -> (2, 3) -> (3, 3) -> (4, 3) -> (4, 4) -> (4, 5) -> (4, 6)
Java
import java.util.*;
class Main {
private static int[][] dir = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public static void main(String[] args) {
int[][] grid = {
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 1, 0},
{0, 0, 1, 0, 1, 1, 0},
{0, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 0, 0, 0, 0}
};
int[][] path = shortestPath(grid);
System.out.println("shortestPath " + Arrays.deepToString(path));
}
public static int[][] shortestPath(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
if (grid == null || grid.length == 0 || grid[0][0] == 1 || grid[m - 1][n - 1] == 1) return new int[][]{{-1, -1}};
int[][] path = bfs_with_visited(grid, m, n);
return path;
}
private static int[][] bfs_with_visited(int[][] grid, int m, int n) {
boolean[][] visited = new boolean[m][n];
visited[0][0] = true;
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{0, 0});
List<int[]> result = new ArrayList<>();
int path = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] currentCell = queue.remove();
result.add(new int[]{currentCell[0], currentCell[1]});
if (currentCell[0] == m - 1 && currentCell[1] == n - 1) {
return result.toArray(new int[result.size()][2]);
}
visitNeighbours(grid, m, n, visited, queue, currentCell);
}
path++;
}
return new int[][]{{-1, -1}};
}
private static void visitNeighbours(int[][] grid, int m, int n, boolean[][] visited, Queue<int[]> queue, int[] currentCell) {
for (int k = 0; k < dir.length; k++) {
int nextX = dir[k][0] + currentCell[0];
int nextY = dir[k][1] + currentCell[1];
if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && !visited[nextX][nextY] && grid[nextX][nextY] == 0) {
queue.add(new int[]{nextX, nextY});
visited[nextX][nextY] = true;
}
}
}
}
感谢您的意见,TIA。
在您的 python 程序中,如果您在 arr = deque.popleft()
之后放置打印语句,您将看到以下内容:
[(0, 0)]
[(0, 0), (1, 0)]
[(0, 0), (0, 1)]
[(0, 0), (1, 0), (2, 0)]
[(0, 0), (1, 0), (1, 1)]
[(0, 0), (0, 1), (1, 1)]
[(0, 0), (0, 1), (0, 2)]
[(0, 0), (1, 0), (2, 0), (3, 0)]
[(0, 0), (1, 0), (2, 0), (2, 1)]
[(0, 0), (1, 0), (1, 1), (2, 1)]
[(0, 0), (0, 1), (1, 1), (2, 1)]
[(0, 0), (0, 1), (0, 2), (0, 3)]
[(0, 0), (1, 0), (2, 0), (3, 0), (3, 1)]
[(0, 0), (1, 0), (2, 0), (2, 1), (3, 1)]
[(0, 0), (1, 0), (1, 1), (2, 1), (3, 1)]
[(0, 0), (0, 1), (1, 1), (2, 1), (3, 1)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (1, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 6), (2, 6)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5), (3, 5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5), (4, 6)]
如果你看到,python 程序正在将一个列表附加到队列中,而不仅仅是一个坐标,这意味着它会记住它经过的路径,或者换句话说,它会一直携带路径。
如果你想在Java中模拟同样的事情,你可以按如下方式进行:
private static int[][] bfs_with_visited(int[][] grid, int m, int n) {
boolean[][] visited = new boolean[m][n];
visited[0][0] = true;
Deque<List<int[]>> queue = new ArrayDeque<>();
List<int[]> result = new ArrayList<>();
result.add(new int[] {0,0});
queue.addFirst(result);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i <size; i++ ) {
result = queue.removeFirst();
int[] curr = result.get(result.size()-1);
if (curr[0] == m-1 && curr[1] == n-1) {
return result.toArray(new int[result.size()][2]);
}
visited[curr[0]][curr[1]] = true;
visitNeighbours(grid, m, n, visited, queue, result, curr);
}
}
return new int[][]{{-1, -1}};
}
private static void visitNeighbours(int[][] grid, int m, int n, boolean[][] visited, Deque<List<int[]>> queue,
List<int[]> listSoFar, int[] currentCell) {
for (int k = 0; k < dir.length; k++) {
int nextX = dir[k][0] + currentCell[0];
int nextY = dir[k][1] + currentCell[1];
if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && !visited[nextX][nextY] && grid[nextX][nextY] == 0) {
List<int[]> l = new ArrayList<>(listSoFar);
l.add(new int[] {nextX, nextY});
queue.add(l);
}
}
}
但我不会建议这样做。而是创建一个 class like
class Node {
int[] prev = null;
int[] curr = null;
public Node(int[] curr, int[] prev) {
this.curr = curr;
this.prev = prev;
}
}
并使用它来填充您的队列。当您找到目标 Node
时,使用 prev
字段跟踪路径,直到到达空 prev
.