752. 打开转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字:
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'。每个拨轮可以自由旋转:例如把'9'变为'0','0'变为'9'。每次旋转都只能旋转一个拨轮的一位数字。 锁的初始数字为'0000',一个代表四个拨轮的数字的字符串。 列表deadends包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。 字符串target代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
示例 1:
输入:deadends = [“0201”,”0101”,”0102”,”1212”,”2002”], target = “0202”
输出:6解释:可能的移动序列为 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。 注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 这样的序列是不能解锁的, 因为当拨动到 “0102” 时这个锁就会被锁定。
示例 2:
输入: deadends = [“8888”], target = “0009”
输出:1解释:把最后一位反向旋转一次即可 “0000” -> “0009”。
示例 3:
输入: deadends = [“8887”,”8889”,”8878”,”8898”,”8788”,”8988”,”7888”,”9888”], target = “8888”
输出:-1解释:无法旋转到目标数字且不被锁定。
示例 4:
输入: deadends = [“0000”], target = “8888”
输出:-1提示:
- 死亡列表
deadends的长度范围为[1, 500]。- 目标数字
target不会在deadends之中。- 每个
deadends和target中的字符串的数字会在 10,000 个可能的情况'0000'到'9999'中产生。
解题思路
BFS
一个密码,只转一下,总共有4个位置,每个位置可以向上转,也可以向下转,一共有8种可能。
比如说从"0000"开始,转一次,可以穷举出"1000", "9000", "0100", "0900"...共 8 种密码。然后,再以这 8 种密码作为基础,对每个密码再转一下,穷举出所有可能…
仔细想想,这就可以抽象成一幅图,每个节点有 8 个相邻的节点,又让你求最短距离,这不就是典型的 BFS 嘛,BFS的框架就可以派上用场了。
还有如下问题需要解决:
1、会走回头路。比如说我们从"0000"拨到"1000",但是等从队列拿出"1000"时,还会拨出一个"0000",这样的话会产生死循环,所以应该有一个集合记录已经访问过的密码。
2、终止条件,按照题目要求,我们找到target就应该结束并返回拨动的次数。
3、对deadends的处理,按道理这些「死亡密码」是不能出现的,也就是说你遇到这些密码的时候需要跳过。
其他:
C++ vector查找是否访问过当前密码和是否在死亡队列里会超时,换成底层使用hash实现的unordered_set,可以大大加速查询
class Solution {public:int openLock(vector<string>& deadends, string target) {unordered_set<string> visited;unordered_set<string> deads(deadends.begin(),deadends.end());//vector转hash,加速查询queue<string> que;int step = 0;que.push("0000");visited.insert("0000");int queSize;string cur;while (!que.empty()) {queSize = que.size();for (int i = 0;i < queSize;i++) {cur = que.front();que.pop();if (deads.count(cur)) continue; //当前密码包含在死亡队列里,跳过这个选择//到达目标了,直接返回if (cur == target) return step;//四个拨轮都轮着拨一遍for (int j = 0;j < 4;j++) {string output1 = upMove(cur,j);if ( !visited.count(output1) ) {visited.insert(output1);que.push(output1);}string output2 = downMove(cur,j);if ( !visited.count(output2) ) {visited.insert(output2);que.push(output2);}}}step++;}return -1;}string upMove(string cur,int pos) {//向上拨cur[pos] = (cur[pos] == '9')? '0':cur[pos] + 1;return cur;}string downMove(string cur,int pos) {//向下拨cur[pos] = (cur[pos] == '0')? '9':cur[pos] - 1;return cur;}};
