题目:
一个密码锁由 4 个环形拨轮组成,每个拨轮都有 10 个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,请给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
示例 :
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,因为当拨动到 "0102" 时这个锁就会被锁定。
思路:
广度优先遍历,可以写双向效率会好一点,懒得写了
复杂度:
时间:单循环O( n )。
空间:O( n )。
代码:
public int openLock(String[] deadends, String target) {
//dead来存放死锁状态
Set<String> dead = new HashSet<>(Arrays.asList(deadends));
//visited 表示访问过的密码
Set<String> visited = new HashSet<>();
//定义初始值
String init ="0000";
//死锁里有初始值和目标值都退出
if(dead.contains(init) || dead.contains(target)){
return -1;
}
Queue<String> queue1 = new LinkedList<>();
Queue<String> queue2 = new LinkedList<>();
//初始长度0
int step = 0;
//将初始值压入队列
queue1.offer(init);
visited.add(init);
while(!queue1.isEmpty()){
//拿queue1中的元素比较
String cur = queue1.remove();
if(cur.equals(target)){
return step;
}
List<String> nexts = getNeighbors(cur);
for(String next :nexts){
if(!dead.contains(next) && !visited.contains(next)){
queue2.add(next);
visited.add(next);
}
}
if(queue1.isEmpty()){
step++;
queue1 = queue2;
queue2 = new LinkedList<>();
}
}
return -1;
}
//写函数来得到与某个状态相连的八个状态
private List<String> getNeighbors(String cur){
List<String> nexts = new LinkedList<>();
for(int i =0;i<cur.length();++i){
char ch = cur.charAt(i);
char newCh = ch == '0'?'9':(char)(ch-1);
StringBuilder builder = new StringBuilder(cur);
builder.setCharAt(i,newCh);
nexts.add(builder.toString());
newCh = ch == '9'?'0':(char)(ch+1);
builder.setCharAt(i,newCh);
nexts.add(builder.toString());
}
return nexts;
}