leetcode题目链接:
752.open the Lock
https://leetcode.com/problems/open-the-lock/description/
遇到一个很有意思的算法,Bidirectional Search.通常我们做bfs都是从原点到目的地单向的遍历,而Bidirectional Search从原点和目的地双向遍历,直到遍历的节点相交,因此效率有较大提升。
1.My ordinary BFS code:
class Solution {
public int openLock(String[] deadends, String target) {
int[][] dirs = {{0, 1}, {0, -1}, {1, 1}, {1, -1}, {2, 1}, {2, -1}, {3, 1}, {3, -1}};
Queue<String> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
for(String end : deadends) visited.add(end);
if(visited.contains("0000")) return -1;
queue.add("0000");
visited.add("0000");
int minmoves = 0;
while(!queue.isEmpty()){
int size = queue.size();
while(size > 0){
String start = queue.poll();
if(start.equals(target)) return minmoves;
for(int[] dir : dirs){
String after = transform(start, dir);
if(!visited.contains(after)){
queue.offer(after);
visited.add(after);
}
}
size--;
}
minmoves++;
}
return -1;
}
public String transform(String str, int[] dir){
char[] cArr = str.toCharArray();
if(cArr[dir[0]] == '0' && dir[1] == -1){
cArr[dir[0]] = '9';
}else if(cArr[dir[0]] == '9' && dir[1] == 1){
cArr[dir[0]] = '0';
}else if(dir[1] == 1){
cArr[dir[0]]++;
}else{
cArr[dir[0]]--;
}
return new String(cArr);
}
}
2-end BFS:
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> begin = new HashSet<>();
Set<String> end = new HashSet<>();
Set<String> deads = new HashSet<>(Arrays.asList(deadends));
begin.add("0000");
end.add(target);
int level = 0;
while(!begin.isEmpty() && !end.isEmpty()) {
Set<String> temp = new HashSet<>();
for(String s : begin) {
if(end.contains(s)) return level;
if(deads.contains(s)) continue;
deads.add(s);
StringBuilder sb = new StringBuilder(s);
for(int i = 0; i < 4; i ++) {
char c = sb.charAt(i);
String s1 = sb.substring(0, i) + (c == '9' ? 0 : c - '0' + 1) + sb.substring(i + 1);
String s2 = sb.substring(0, i) + (c == '0' ? 9 : c - '0' - 1) + sb.substring(i + 1);
if(!deads.contains(s1))
temp.add(s1);
if(!deads.contains(s2))
temp.add(s2);
}
}
level ++;
begin = end;
end = temp;
}
return -1;
}
}
optimized 2-end BFS:
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> begin = new HashSet<>();
Set<String> end = new HashSet<>();
Set<String> deads = new HashSet<>(Arrays.asList(deadends));
begin.add("0000");
end.add(target);
int level = 0;
Set<String> temp;
while(!begin.isEmpty() && !end.isEmpty()) {
//优化的地方在这里,总是从较少元素的那一端开始遍历
if (begin.size() > end.size()) {
temp = begin;
begin = end;
end = temp;
}
temp = new HashSet<>();
for(String s : begin) {
if(end.contains(s)) return level;
if(deads.contains(s)) continue;
deads.add(s);
StringBuilder sb = new StringBuilder(s);
for(int i = 0; i < 4; i ++) {
char c = sb.charAt(i);
String s1 = sb.substring(0, i) + (c == '9' ? 0 : c - '0' + 1) + sb.substring(i + 1);
String s2 = sb.substring(0, i) + (c == '0' ? 9 : c - '0' - 1) + sb.substring(i + 1);
if(!deads.contains(s1)) temp.add(s1);
if(!deads.contains(s2)) temp.add(s2);
}
}
level++;
begin = temp;
}
return -1;
}
}
如果对BFS和Bidirectional Search感兴趣的话,以下两个链接可供你参考:
1.一个leetcode上面关于BFS和2-end BFS的总结:
https://leetcode.com/problems/open-the-lock/discuss/110237/Regular-java-BFS-solution-and-2-end-BFS-solution-with-improvement
2.geekbygeek上面关于Bidirectional Search的算法和描述:
https://www.geeksforgeeks.org/bidirectional-search/