题目地址:
https://www.lintcode.com/problem/the-maze-ii/description
给定一个二维数组,其表示一个迷宫, 代表空地, 代表障碍物,不含其它数字。再给定一个出发点和一个终点,题目保证出发点和终点都是空地。现在有一个小球从出发点出发,其可沿着四个方向直线滑行,直到遇到边界或者障碍物才能停下来。问是否存在一种滑行的可能,使得小球停在终点。
本质上是带权图求最短路,所以用Dijkstra算法。用一个优先队列记录小球每次滑行所滑到的目标点,然后再用一个数组来记录某个点是否之前滑到过。如果终点到了直接返回最短路径长度,否则继续尝试四个方向的滑行,直到所有可能的点都访问完毕为止。代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
public class Solution {
// Pair是迷宫里的每个点的抽象出来的class,x和y代表坐标,dist代表离start的最少步数
class Pair {
int x, y, dist;
Pair(int x, int y, int dist) {
this.x = x;
this.y = y;
this.dist = dist;
}
}
/**
* @param maze: the maze
* @param start: the start
* @param destination: the destination
* @return: the shortest distance for the ball to stop at the destination
*/
public int shortestDistance(int[][] maze, int[] start, int[] destination) {
// write your code here
// 开一个优先队列,使得离start近的点先出队
PriorityQueue<Pair> queue = new PriorityQueue<>((v1, v2) -> v1.dist < v2.dist ? -1 : 1);
boolean[][] visited = new boolean[maze.length][maze[0].length];
queue.offer(new Pair(start[0], start[1], 0));
visited[start[0]][start[1]] = true;
while (!queue.isEmpty()) {
Pair cur = queue.poll();
List<Pair> nexts = getNext(cur, maze, visited);
for (Pair next : nexts) {
if (next.x == destination[0] && next.y == destination[1]) {
return next.dist;
}
queue.offer(next);
visited[next.x][next.y] = true;
}
}
return -1;
}
// 返回从cur出发可以到达的且未被访问过的点
private List<Pair> getNext(Pair cur, int[][] maze, boolean[][] visited) {
int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
List<Pair> dest = new ArrayList<>();
for (int i = 0; i < 4; i++) {
int x = cur.x, y = cur.y, dist = 0;
while (inBound(x, y, maze) && maze[x][y] != 1) {
x += dirs[i][0];
y += dirs[i][1];
dist++;
}
x -= dirs[i][0];
y -= dirs[i][1];
dist--;
if (!visited[x][y]) {
dest.add(new Pair(x, y, cur.dist + dist));
}
}
return dest;
}
private boolean inBound(int x, int y, int[][] maze) {
return 0 <= x && x < maze.length && 0 <= y && y < maze[0].length;
}
}
时间复杂度 ,空间 。