Leetcode.752 打开转盘锁

1. 思路

我们可以使用广度优先搜索,找出从初始数字 0000到解锁数字 target 的最小旋转次数。
具体地,我们在一开始将 (0000,0) 加入队列,并使用该队列进行广度优先搜索。在搜索的过程中,设当前搜索到的数字为 status,旋转的次数为 step,我们可以枚举 status 通过一次旋转得到的数字。设其中的某个数字为 next_status,如果其没有被搜索过,我们将(next_status,step+1) 加入队列。如果搜索到了 target,我们就返回其对应的旋转次数。
为了避免搜索到死亡数字,我们可以使用哈希表存储 deadends 中的所有元素,这样在搜索的过程中,我们可以均摊 O(1) 地判断一个数字是否为死亡数字。同时,我们还需要一个哈希表存储所有搜索到的状态,避免重复搜索。

2. 代码

  1. class Solution {
  2. public int openLock(String[] deadends, String target) {
  3. if (target.equals("0000")) {
  4. return 0;
  5. }
  6. Set<String> dead = new HashSet<String>();
  7. for (String deaded : deadends) {
  8. dead.add(deaded);
  9. }
  10. if(dead.contains("0000")){
  11. return -1;
  12. }
  13. int step =0;
  14. Queue<String> queue = new LinkedList<String>();
  15. queue.add("0000");
  16. Set<String> seen = new HashSet<String>();
  17. seen.add("0000");
  18. while (!queue.isEmpty()){
  19. step++;
  20. int size = queue.size();
  21. for(int i=0;i<size;i++){
  22. String cur = queue.poll();
  23. for(String nextString:get(cur)){
  24. if(!seen.contains(nextString)&&!dead.contains(nextString)){
  25. if(nextString.equals(target)){
  26. return step;
  27. }
  28. queue.add(nextString);
  29. seen.add(nextString);
  30. }
  31. }
  32. }
  33. }
  34. return -1;
  35. }
  36. public char numPrev(char x) {
  37. return x == '0' ? '9' : (char) (x - 1);
  38. }
  39. public char numSucc(char x) {
  40. return x == '9' ? '0' : (char) (x + 1);
  41. }
  42. public List<String> get(String status) {
  43. List<String> ret = new ArrayList<String>();
  44. char[] array = status.toCharArray();
  45. for (int i = 0; i < 4; ++i) {
  46. char num = array[i];
  47. array[i] = numPrev(num);
  48. ret.add(new String(array));
  49. array[i] = numSucc(num);
  50. ret.add(new String(array));
  51. array[i] = num;
  52. }
  53. return ret;
  54. }
  55. }

Leetcode.773 滑动谜题

1. 思路

我们可以使用广度优先搜索,找出从初始状态 board 到目标状态 广度优先搜索(BFS) - 图1 的最小交换次数。
具体地,我们在一开始将 广度优先搜索(BFS) - 图2 加入队列,并使用该队列进行广度优先搜索。在搜索的过程中,设当前搜索到的状态为 广度优先搜索(BFS) - 图3,操作的次数为 广度优先搜索(BFS) - 图4,我们可以枚举 status 通过一次操作得到的状态。设其中的某个状态为 next_status,如果其没有被搜索过,我们就将 广度优先搜索(BFS) - 图5 加入队列。如果搜索到了 广度优先搜索(BFS) - 图6,我们就返回其对应的操作次数。
在搜索的过程中,我们需要一个哈希表存储所有搜索到的状态,避免重复搜索。
如果搜索完成后,我们仍没有搜索到 广度优先搜索(BFS) - 图7,说明我们无法解开谜板,返回 -1。

2.代码

  1. class Solution {
  2. int[][] neighbors = {{1, 3}, {0, 2, 4}, {1, 5}, {0, 4}, {1, 3, 5}, {2, 4}};
  3. public int slidingPuzzle(int[][] board) {
  4. StringBuffer stringBuffer = new StringBuffer();
  5. for (int i = 0; i < 2; i++) {
  6. for (int j = 0; j < 3; j++) {
  7. stringBuffer.append(board[i][j]);
  8. }
  9. }
  10. String initial = stringBuffer.toString();
  11. if (initial.equals("123450")) {
  12. return 0;
  13. }
  14. int step = 0;
  15. Queue<String> queue = new LinkedList<String>();
  16. queue.offer(initial);
  17. Set<String> seen = new HashSet<String>();
  18. seen.add(initial);
  19. while (!queue.isEmpty()) {
  20. step++;
  21. int size = queue.size();
  22. for (int i = 0; i < size; i++) {
  23. String cur = queue.poll();
  24. for (String nextStatus : get(cur)) {
  25. if (!seen.contains(nextStatus)) {
  26. if (nextStatus.equals("123450")) {
  27. return step;
  28. }
  29. queue.offer(nextStatus);
  30. seen.add(nextStatus);
  31. }
  32. }
  33. }
  34. }
  35. return -1;
  36. }
  37. private List<String> get(String cur) {
  38. List<String> stringList = new ArrayList<String>();
  39. char[] chars = cur.toCharArray();
  40. int index = cur.indexOf('0');
  41. for (int y : neighbors[index]) {
  42. swap(chars, index, y);
  43. stringList.add(String.valueOf(chars));
  44. swap(chars, index, y);
  45. }
  46. return stringList;
  47. }
  48. public void swap(char[] array, int x, int y) {
  49. char temp = array[x];
  50. array[x] = array[y];
  51. array[y] = temp;
  52. }
  53. }

Leetcode. 909 蛇题棋

1. 思路

对于该问题,我们可以使用广度优先搜索。将节点编号和到达该节点的移动次数作为搜索状态,顺着该节点的出边扩展新状态,直至到达终点 广度优先搜索(BFS) - 图8 ,返回此时的移动次数。若无法到达终点则返回 -1

2. 代码

  1. class Solution {
  2. public int snakesAndLadders(int[][] board) {
  3. int n = board.length;
  4. boolean[] vis = new boolean[n * n + 1];
  5. Queue<int[]> queue = new LinkedList<int[]>();
  6. queue.offer(new int[]{1, 0});
  7. while (!queue.isEmpty()) {
  8. int[] p = queue.poll();
  9. for (int i = 1; i <= 6; ++i) {
  10. int nxt = p[0] + i;
  11. if (nxt > n * n) { // 超出边界
  12. break;
  13. }
  14. int[] rc = id2rc(nxt, n); // 得到下一步的行列
  15. if (board[rc[0]][rc[1]] > 0) { // 存在蛇或梯子
  16. nxt = board[rc[0]][rc[1]];
  17. }
  18. if (nxt == n * n) { // 到达终点
  19. return p[1] + 1;
  20. }
  21. if (!vis[nxt]) {
  22. vis[nxt] = true;
  23. queue.offer(new int[]{nxt, p[1] + 1}); // 扩展新状态
  24. }
  25. }
  26. }
  27. return -1;
  28. }
  29. public int[] id2rc(int id, int n) {
  30. int r = (id - 1) / n, c = (id - 1) % n;
  31. if (r % 2 == 1) {
  32. c = n - 1 - c;
  33. }
  34. return new int[]{n - 1 - r, c};
  35. }
  36. }