题目
在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字(上下左右)进行交换.
最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。
给出一个谜板的初始状态 board ,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。
示例 1:

1 2 3
| 输入:board = [[1,2,3],[4,0,5]] 输出:1 解释:交换 0 和 5 ,1 步完成
|
示例 2:

1 2 3
| 输入:board = [[1,2,3],[5,4,0]] 输出:-1 解释:没有办法完成谜板
|
示例 3:

1 2 3 4 5 6 7 8 9 10 11
| 输入: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] 中每个值都 不同
实现代码
广度优先算法(BFS),0在不同位置所相邻的数字块为树的分支,因为是求最值,所以使用BFS算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| public int slidingPuzzle(int[][] board) {
int[][] adjoinIndexArray = new int[][]{ {1,3}, {0,2,4}, {1,5}, {0,4}, {1,3,5}, {2,4}}; Set<String> visited = new HashSet<>(); String initialState = array2Str(board); visited.add(initialState); Queue<String> queue = new LinkedList<>(); queue.offer(initialState); int steps = 0; while (!queue.isEmpty()) { int levelSize = queue.size(); for (int i = 0; i < levelSize; i++) { String currentState = queue.poll(); if ("123450".equals(currentState)) { return steps; } int zeroIndex = zeroIndex(currentState); int[] adjoinIndex = adjoinIndexArray[zeroIndex]; for (int index : adjoinIndex) { String newState = swap(currentState, zeroIndex, index); if (!visited.contains(newState)) { queue.offer(newState); visited.add(newState); } } } steps++; } return -1; }
private String array2Str(int[][] array) {
StringBuilder sb = new StringBuilder(); for (int[] ints : array) { for (int anInt : ints) { sb.append(anInt); } } return sb.toString(); }
private int zeroIndex(String str) {
char[] charArray = str.toCharArray(); for (int i = 0; i < charArray.length; i++) { char c = charArray[i]; if (c == '0') { return i; } } return -1; }
private String swap(String str, int zeroIndex, int adjoinIndex) {
char[] charArray = str.toCharArray(); char tmp = charArray[zeroIndex]; charArray[zeroIndex] = charArray[adjoinIndex]; charArray[adjoinIndex] = tmp; return new String(charArray); }
|
代码讲解
1 2 3 4 5 6 7 8
| int[][] adjoinIndexArray = new int[][]{ {1,3}, {0,2,4}, {1,5}, {0,4}, {1,3,5}, {2,4}};
|
当0在索引0位置,则相邻块为索引1、3位置;

当0在索引1位置,则相邻块为索引0、4、2位置;

当0在索引2位置,则相邻块为索引1、5位置;

当0在索引3位置,则相邻块为索引0、4位置;

当0在索引4位置,则相邻块为索引3、1、5位置;

当0在索引5位置,则相邻块为索引2、4位置;

时间复杂度
对于2×3的滑动谜题,总共有6!种可能的状态(因为有6个位置,每个数字0-5都有固定位置)。在最坏情况下,BFS需要遍历所有可能的状态。每个状态需要检查相邻位置(最多4个),每次状态转换的时间复杂度为O(1)(6个)。因此总的时间复杂度为O(6!) = O(720)。虽然在数学上这是一个常数,但对于一般的m×n滑动谜题,时间复杂度应该是O((m×n)!)。
空间复杂度
主要的额外空间消耗来自:
visited 集合:在最坏情况下需要存储所有可能的状态,空间复杂度为O(6!) = O(720)
- BFS队列:在最坏情况下可能包含一层的所有状态,空间复杂度也是
O(6!)
因此总的空间复杂度为O(6!),对于一般的m×n滑动谜题,空间复杂度为O((m×n)!)。