题目地址:
https://leetcode.com/problems/sliding-puzzle/
给定一个 2 × 3 2\times 3 2×3的二维数组,数字为 0 0 0到 5 5 5,代表一个盘面,每次可以将一个 0 0 0紧挨着的数字和 0 0 0交换,问最少需要多少步可以到达 123456 123456 123456这个盘面。
基本思路是BFS。将盘面考虑成一个状态,我们只需要对这些状态进行BFS即可。为了状态存储的方便,我们考虑用字符串来代表每个状态来做状态压缩。代码如下:
import java.util.*;
public class Solution {
public int slidingPuzzle(int[][] board) {
Queue<String> queue = new LinkedList<>();
// visited的key记录已经访问过的状态,value记录从初始状态要走多少步到达当前状态
Map<String, Integer> visited = new HashMap<>();
String init = boardToString(board);
// 如果初始状态就是目标状态,那直接返回0
if (init.equals("123450")) {
return 0;
}
queue.offer(init);
visited.put(init, 0);
while (!queue.isEmpty()) {
String cur = queue.poll();
List<String> nexts = getNexts(cur);
for (String next : nexts) {
if (!visited.containsKey(next)) {
// 如果cur的某个邻居就是目标状态,直接返回路径长度
if (next.equals("123450")) {
return visited.get(cur) + 1;
}
queue.offer(next);
visited.put(next, visited.get(cur) + 1);
}
}
}
return -1;
}
// 计算状态s的邻居状态
private List<String> getNexts(String s) {
// 先将s转化为盘面
int[][] cur = stringToBoard(s);
// 接下来先找0
int zero;
for (zero = 0; zero < 6; zero++) {
if (cur[zero / 3][zero % 3] == 0) {
break;
}
}
// 开个二维数组表示四个相邻的方向
int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// 存储邻居状态
List<String> res = new ArrayList<>();
int zx = zero / 3, zy = zero % 3;
for (int i = 0; i < 4; i++) {
int nextx = zx + dirs[i][0], nexty = zy + dirs[i][1];
if (inBoard(nextx, nexty)) {
// 交换0和邻居,得到新的状态
swap(cur, zx, zy, nextx, nexty);
res.add(boardToString(cur));
// 注意,一定恢复成原来的状态,然后再产生下一个状态
swap(cur, zx, zy, nextx, nexty);
}
}
return res;
}
private void swap(int[][] board, int x1, int y1, int x2, int y2) {
int tmp = board[x1][y1];
board[x1][y1] = board[x2][y2];
board[x2][y2] = tmp;
}
private boolean inBoard(int x, int y) {
return 0 <= x && x < 2 && 0 <= y && y < 3;
}
private int[][] stringToBoard(String s) {
int[][] board = new int[2][3];
for (int i = 0; i < s.length(); i++) {
board[i / 3][i % 3] = s.charAt(i) - '0';
}
return board;
}
private String boardToString(int[][] board) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
sb.append(board[i][j]);
}
}
return sb.toString();
}
}
时空复杂度 O ( V ! ) O(V!) O(V!), V V V代表状态数量。