滑动谜题
难度:
标签:
题目描述
在一个 2 x 3
的板上(board
)有 5 块砖瓦,用数字 1~5
来表示, 以及一块空缺用 0
来表示。一次 移动 定义为选择 0
与一个相邻的数字(上下左右)进行交换.
最终当板 board
的结果是 [[1,2,3],[4,5,0]]
谜板被解开。
给出一个谜板的初始状态 board
,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1
。
示例 1:
输入:board = [[1,2,3],[4,0,5]] 输出:1 解释:交换 0 和 5 ,1 步完成
示例 2:
输入:board = [[1,2,3],[5,4,0]] 输出:-1 解释:没有办法完成谜板
示例 3:
输入:board = [[4,1,2],[5,0,3]] 输出:5 解释: 最少完成谜板的最少移动次数是 5 , 一种移动路径: 尚未移动: [[4,1,2],[5,0,3]] 移动 1 次: [[4,1,2],[0,5,3]] 移动 2 次: [[0,1,2],[4,5,3]] 移动 3 次: [[1,0,2],[4,5,3]] 移动 4 次: [[1,2,0],[4,5,3]] 移动 5 次: [[1,2,3],[4,5,0]]
提示:
board.length == 2
board[i].length == 3
0 <= board[i][j] <= 5
board[i][j]
中每个值都 不同
代码结果
运行时间: 28 ms, 内存: 0.0 MB
/*
题目思路:
使用Java Stream API来实现广度优先搜索(BFS)。
我们需要使用Stream来处理队列的操作,具体步骤与常规Java解法类似,只是使用Stream来替代部分操作。
1. 将初始状态转换成字符串以便处理。
2. 使用Stream来创建和处理队列,进行BFS搜索。
3. 在每次循环中,使用Stream来处理当前状态,生成新的状态。
4. 记录已经访问过的状态,避免重复处理。
5. 最终返回最少的移动次数,若找不到目标状态则返回-1。
*/
import java.util.*;
import java.util.stream.*;
public class SlidingPuzzleStream {
private static final String TARGET = "123450";
private static final int[][] NEIGHBORS = {{1, 3}, {0, 2, 4}, {1, 5}, {0, 4}, {1, 3, 5}, {2, 4}};
public int slidingPuzzle(int[][] board) {
StringBuilder sb = new StringBuilder();
for (int[] row : board) {
for (int num : row) {
sb.append(num);
}
}
String start = sb.toString();
if (start.equals(TARGET)) return 0;
Set<String> visited = new HashSet<>();
visited.add(start);
List<String> queue = Collections.singletonList(start);
int moves = 0;
while (!queue.isEmpty()) {
List<String> nextQueue = new ArrayList<>();
for (String current : queue) {
if (current.equals(TARGET)) return moves;
int zeroIndex = current.indexOf('0');
for (int neighbor : NEIGHBORS[zeroIndex]) {
String next = swap(current, zeroIndex, neighbor);
if (visited.add(next)) {
nextQueue.add(next);
}
}
}
queue = nextQueue;
moves++;
}
return -1;
}
private String swap(String s, int i, int j) {
char[] chars = s.toCharArray();
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
return new String(chars);
}
}
解释
方法:
本题解采用广度优先搜索(BFS)来解决滑动谜题。首先,将二维数组board转换为一维状态字符串,例如[[1,2,3],[4,0,5]]转化为'123405'。设定目标状态为'123450'。使用队列q来存储每一步的状态及其对应的移动次数,同时使用集合seen来记录已经访问过的状态,避免重复处理。对于当前状态,通过交换0与其相邻位置的数字来生成新的状态,并检查是否已达到目标状态或是否已被访问过。若达到目标状态,则返回对应的移动次数。如果队列被耗尽还未找到目标状态,则返回-1表示无法解开谜题。
时间复杂度:
O(1)
空间复杂度:
O(1)
代码细节讲解
🦆
为什么选择广度优先搜索(BFS)而不是深度优先搜索(DFS)来解决这个滑动谜题?
▷🦆
在使用BFS算法时,你如何处理可能重复的状态,以避免不必要的计算?
▷🦆
在`get`函数中,如何确定0的邻居位置,并且为什么选择使用这种特定的邻居关系数组`NEIGHBORS`?
▷🦆
为什么在初始状态即为目标状态'123450'时,直接返回0而不进一步执行搜索过程?
▷