剑指 Offer II 109. 开密码锁(752. 打开转盘锁)
题目:
思路:
【1】广度优先搜索:
代码展示:
//时间63 ms击败79.94% //内存45 MB击败74.14% class Solution { public int openLock(String[] deadends, String target) { if ("0000".equals(target)) { return 0; } Set<String> dead = new HashSet<String>(); for (String deadend : deadends) { dead.add(deadend); } if (dead.contains("0000")) { return -1; } int step = 0; Queue<String> queue = new LinkedList<String>(); queue.offer("0000"); Set<String> seen = new HashSet<String>(); seen.add("0000"); while (!queue.isEmpty()) { ++step; int size = queue.size(); for (int i = 0; i < size; ++i) { String status = queue.poll(); for (String nextStatus : get(status)) { if (!seen.contains(nextStatus) && !dead.contains(nextStatus)) { if (nextStatus.equals(target)) { return step; } queue.offer(nextStatus); seen.add(nextStatus); } } } } return -1; } public char numPrev(char x) { return x == '0' ? '9' : (char) (x - 1); } public char numSucc(char x) { return x == '9' ? '0' : (char) (x + 1); } // 枚举 status 通过一次旋转得到的数字 public List<String> get(String status) { List<String> ret = new ArrayList<String>(); char[] array = status.toCharArray(); for (int i = 0; i < 4; ++i) { char num = array[i]; array[i] = numPrev(num); ret.add(new String(array)); array[i] = numSucc(num); ret.add(new String(array)); array[i] = num; } return ret; } }
//时间24 ms击败88.20% //内存42 MB击败94.55% class Solution { public int openLock(String[] deadends, String target) { // 思路:从0000开始,找相邻的密码,比如这个时候就有八个 // 9000、1000 // 0900、0100 // 0090、0010 // 0009、0001 // 然后将这八个结果存入队列中,其中每个相邻的结果后面还会有八个 // 当然要是在这期间遇上了是死亡数字或者是已经被访问过的,就不加入队列
//(这有两种方法,前面的是第一种,从0000开始找target,还有一种就是从target和0000两个方向开始找,用两个set保存,当这个set的邻居结果在另一个set中,表示已找到) // 以下采用第二种 Set<String> deadSet = new HashSet<>(); for(String deadStr : deadends) deadSet.add(deadStr); if(deadSet.contains("0000")) return -1; if("0000".equals(target)) return 0; Set<String> st1 = new HashSet<>();//用来记录从0000开始的令居 Set<String> st2 = new HashSet<>();//用来从target开始的邻居 Set<String> moved = new HashSet<>();//表示已经访问过的 st1.add("0000"); st2.add(target); int step = 0; while(!st1.isEmpty() && !st2.isEmpty()){ step++; if(st1.size() > st2.size()){ // 从短的开始找邻居,这样复杂度更低 Set<String> temp = st1; st1 = st2; st2 = temp; } Set<String> st3 = new HashSet<>();//用来记录新的邻居 for(String str : st1){ Set<String> neighbourSet = getNeighbour(str);//拿到了所有的子串 for(String neighbourStr : neighbourSet){ // 检查是否有符合条件的或者已经遍历过的 if(deadSet.contains(neighbourStr)) continue; if(st2.contains(neighbourStr)) return step;//当这个值出现在了两个set,则表示有重合,即出现过 if(!moved.contains(neighbourStr)){ st3.add(neighbourStr); moved.add(neighbourStr); } } } st1 = st3; } // 没找到则返回-1 return -1; } public Set<String> getNeighbour(String str){ Set<String> st3 = new HashSet<>(); StringBuilder sb = new StringBuilder(); sb.append(str); char[] ch = str.toCharArray(); for(int i = 0; i < str.length(); i++){ char curC = ch[i]; // 向下转 curC = ch[i] == '0' ? '9' : (char)(ch[i] - 1); sb.setCharAt(i,curC); st3.add(sb.toString()); // 向上转 curC = ch[i] == '9' ? '0' : (char)(ch[i] + 1); sb.setCharAt(i,curC); st3.add(sb.toString()); sb.setCharAt(i,ch[i]); } return st3; } }
//时间13 ms击败99.55% //内存42.2 MB击败90.19% class Solution { // 双向bfs public int openLock(String[] deadends, String target) { if (target.equals("0000")) return 0; // 如果目标就是初始状态,不需要旋转锁,直接返回 0 HashSet<String> ds = new HashSet<>(); // 存放所有不可达的状态,即不能旋转到的状态 for (String d : deadends) ds.add(d); // 将不可达状态加入到集合 ds 中 if (ds.contains("0000")) return -1; // 如果初始状态是不可达状态,那么无法打开锁,直接返回 -1 HashSet<String> visited = new HashSet<>(); visited.add("0000"); HashSet<String> beginVisited = new HashSet<>(); beginVisited.add("0000"); HashSet<String> endVisited = new HashSet<>(); endVisited.add(target); int step = 0; while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { if (beginVisited.size() > endVisited.size()) { HashSet<String> temp = beginVisited; beginVisited = endVisited; endVisited = temp; } HashSet<String> nextVisited = new HashSet<>(); for (String str : beginVisited) { if (curr(str, endVisited, visited, nextVisited, ds)) { return step + 1; } } beginVisited = nextVisited; step++; } return -1; } //可能的变化 public boolean curr(String word, HashSet<String> endVisited, HashSet<String> visited, HashSet<String> nextVisited, HashSet<String> wordset) { char[] chars = word.toCharArray(); for (int i = 0; i < chars.length; i++) { // 遍历当前状态中的每个数字 char num = chars[i]; // 获取当前数字 // 将当前数字向上转动一格 chars[i] = num == '9' ? '0' : (char) (num + 1); // 如果当前数字是 9,那么向上转动一格后变成 0;否则加 1 String str1 = new String(chars); if (!wordset.contains(str1)) { if (endVisited.contains(str1)) { return true; } if (!visited.contains(str1)) { nextVisited.add(str1); visited.add(str1); } } // 将当前数字向下转动一格 chars[i] = num == '0' ? '9' : (char) (num - 1); // 如果当前数字是 0,那么向下转动一格后变成 9;否则减 1 String str2 = new String(chars); if (!wordset.contains(str2)) { if (endVisited.contains(str2)) { return true; } if (!visited.contains(str2)) { nextVisited.add(str2); visited.add(str2); } } chars[i] = num; // 将当前数字重置为原来的值,为下一次循环做准备 } return false; } }
//时间10 ms击败99.85% //内存41.8 MB击败96.73% class Solution { private static final int START = 0; private static final int[] WHEELS = {1000, 100, 10, 1}; private static final int[] DIRECTIONS = {-1, 1}; public int openLock(String[] deadends, String target) { boolean[] dead = new boolean[10000]; for (String deadend : deadends) { dead[Integer.parseInt(deadend)] = true; } if (dead[START]) { return -1; } int fin = Integer.parseInt(target); if (fin == START) { return 0; } int count = 0; dead[START] = dead[fin] = true; Set<Integer> bfs1 = new HashSet<>(Set.of(START)); Set<Integer> bfs2 = new HashSet<>(Set.of(fin)); while (!bfs1.isEmpty() && !bfs2.isEmpty()) { ++count; if (bfs1.size() > bfs2.size()) { Set<Integer> tmp = bfs1; bfs1 = bfs2; bfs2 = tmp; } Set<Integer> nextBfs1 = new HashSet<>(); for (int curr : bfs1) { for (int wheel : WHEELS) { for (int direction : DIRECTIONS) { int currDigit = curr / wheel % 10; int nextDigit = (currDigit + direction + 10) % 10; int next = curr + (nextDigit - currDigit) * wheel; if (bfs2.contains(next)) { return count; } else if (dead[next]) { continue; } dead[next] = true; nextBfs1.add(next); } } } bfs1 = nextBfs1; } return -1; } }