你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
提示:
1 <= deadends.length <= 500
deadends[i].length == 4
target.length == 4
target 不在 deadends 之中
target 和 deadends[i] 仅由若干位数字组成
1、BFS
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> visited = new HashSet<>();
Set<String> dead = new HashSet<>();
for(String s : deadends){
dead.add(s);
}
Queue<String> queue = new LinkedList<>();
queue.add("0000");
int step = 0;
visited.add("0000");
while( !queue.isEmpty() ){
int size = queue.size();
for(int i = 0;i < size;i++){
String temp = queue.poll();
if(dead.contains(temp)){
continue;
}
if(temp.equals(target)){
return step;
}
//有四个数字,每个都有上下两个方向
for(int j = 0;j < 4;j++){
String up = PlusOne(temp,j);
if( !visited.contains(up) ){
visited.add(up);
queue.add(up);
}
String down = MinusOne(temp,j);
if( !visited.contains(down) ){
visited.add(down);
queue.add(down);
}
}
}
++step;
}
return -1;
}
//上移一位
public String PlusOne(String s,int j){
char[] chs = s.toCharArray();
if(chs[j] == '9'){
chs[j] = '0';
}else{
chs[j] += 1;
}
return new String(chs);
}
//下移一位
public String MinusOne(String s,int j){
char[] chs = s.toCharArray();
if(chs[j] == '0'){
chs[j] = '9';
}else{
chs[j] -= 1;
}
return new String(chs);
}
}
执行用时:75 ms, 在所有 Java 提交中击败了48.26%的用户
内存消耗:47.2 MB, 在所有 Java 提交中击败了26.83%的用户
2、双向BFS
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> visited = new HashSet<>();
Set<String> dead = new HashSet<>();
for(String s : deadends){
dead.add(s);
}
//用集合不用队列可以快速判断元素是否存在
Set<String> q_start = new HashSet<>();
Set<String> q_end = new HashSet<>();
q_start.add("0000");
q_end.add(target);
int step = 0;
while( !q_start.isEmpty() && !q_end.isEmpty()){
//哈希集合在遍历过程中不能修改,用temp存储扩散结果
Set<String> temp = new HashSet<>();
//将q_start中的所有结点向周围扩散
for(String cur : q_start){
if(dead.contains(cur)){
continue;
}
//如果结果Set中包含则返回
if(q_end.contains(cur)){
return step;
}
visited.add(cur);
//有四个数字,每个都有上下两个方向
for(int j = 0;j < 4;j++){
String up = PlusOne(cur,j);
if( !visited.contains(up) ){
temp.add(up);
}
String down = MinusOne(cur,j);
if( !visited.contains(down) ){
temp.add(down);
}
}
}
++step;
//交换q_start和q_end,temp即为q_start
//下一轮就是while扩散q_end
q_start = q_end;
q_end = temp;
}
return -1;
}
//上移一位
public String PlusOne(String s,int j){
char[] chs = s.toCharArray();
if(chs[j] == '9'){
chs[j] = '0';
}else{
chs[j] += 1;
}
return new String(chs);
}
//下移一位
public String MinusOne(String s,int j){
char[] chs = s.toCharArray();
if(chs[j] == '0'){
chs[j] = '9';
}else{
chs[j] -= 1;
}
return new String(chs);
}
}
执行用时:22 ms, 在所有 Java 提交中击败了94.46%的用户
内存消耗:41.7 MB, 在所有 Java 提交中击败了94.07%的用户