打开转盘锁
难度:
标签:
题目描述
代码结果
运行时间: 864 ms, 内存: 15.8 MB
/*
* Problem: You have a lock with four circular dials. Each dial has 10 digits: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'.
* The dials can rotate freely: e.g., '9' can turn into '0', and '0' can turn into '9'.
* Each move can rotate one dial by one digit.
* The lock's initial state is '0000'.
* A list called deadends contains numbers that cause the lock to be permanently locked if reached.
* The string target represents the number to unlock the lock. You need to find the minimum number of moves to unlock the lock, or return -1 if it's not possible.
*/
import java.util.*;
import java.util.stream.*;
public class OpenLockStream {
public int openLock(String[] deadends, String target) {
Set<String> deadSet = Arrays.stream(deadends).collect(Collectors.toSet());
if (deadSet.contains("0000")) return -1;
if (target.equals("0000")) return 0;
Set<String> visited = new HashSet<>();
visited.add("0000");
Queue<String> queue = new LinkedList<>();
queue.offer("0000");
int moves = 0;
while (!queue.isEmpty()) {
int size = queue.size();
moves++;
for (int i = 0; i < size; i++) {
String current = queue.poll();
List<String> nextStates = IntStream.range(0, 4)
.mapToObj(j -> Stream.of(-1, 1)
.map(d -> rotate(current, j, d))
.collect(Collectors.toList()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
for (String next : nextStates) {
if (next.equals(target)) return moves;
if (!visited.contains(next) && !deadSet.contains(next)) {
queue.offer(next);
visited.add(next);
}
}
}
}
return -1;
}
private String rotate(String state, int index, int direction) {
char[] chars = state.toCharArray();
chars[index] = (char)((chars[index] - '0' + direction + 10) % 10 + '0');
return new String(chars);
}
}
解释
方法:
此题解采用了宽度优先搜索(BFS)算法来找到从初始状态'0000'到目标状态'target'的最短路径。每个状态表示为一个四位数字的字符串,每次操作可以将任意一位数字增加1或减少1。如果增加或减少后的数字是9或者0,则会循环到0或9。使用队列来存储每一层的所有状态,并用一个集合来记录已访问过的状态和死亡数字。从'0000'开始搜索,每次从队列中取出当前状态,并生成新的状态,如果新状态没有被访问过且不在死亡数字中,则加入队列继续搜索。如果找到目标状态,则返回当前的步数。如果队列为空还没有找到目标状态,返回-1。
时间复杂度:
O(N)
空间复杂度:
O(N)
代码细节讲解
🦆
为什么在宽度优先搜索中使用队列而不是栈?队列和栈在这种搜索中有什么不同的影响?
▷🦆
在检查到当前状态为目标状态时直接返回步数的逻辑是否考虑了所有可能的路径到达目标状态的最小步数?
▷🦆
函数up和down在处理边界条件(数字为0或9时)的策略是什么?这种处理方式是否最优?
▷🦆
代码中有一个检查'0000'是否在deadends中的步骤,如果'0000'在deadends列表中,为什么直接返回-1?
▷