leetcode:752. 打开转盘锁
题目
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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" 时这个锁就会被锁定。
输入: deadends = ["8888"], target = "0009"
输出:1
解释:把最后一位反向旋转一次即可 "0000" -> "0009"。
输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
输出:-1
解释:无法旋转到目标数字且不被锁定。
解答 & 代码
BFS 广度优先搜索(层序遍历):
本题求转盘锁从初始 “0000” 拨出 target 的最少旋转次数,就相当于求图中从起点走到终点的最短路径,就可以用 BFS 解决(更准确的说是层序遍历),广度优先遍历第一次走到 target 的层数就是最短步数
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
// 哈希表,存储死亡密码
unordered_set<string> deadMap;
for(int i = 0; i < deadends.size(); ++i)
deadMap.insert(deadends[i]);
string cur = "0000"; // 初始密码
// 如果初始密码 or 目标密码是死亡密码,则直接返回 -1
if(deadMap.find(cur) != deadMap.end() || deadMap.find(target) != deadMap.end())
return -1;
// BFS 广度优先遍历(层序遍历)
queue<string> q; // 队列
q.push(cur);
unordered_set<string> visitedMap; // 哈希表,记录已穷举过的密码,防止走回头路
visitedMap.insert(cur);
int step = 0; // 步数(层数)
while(!q.empty())
{
int levelCnt = q.size();
// 遍历这一层的所有密码
for(int i = 0; i < levelCnt; ++i)
{
cur = q.front();
q.pop();
// 如果当前密码就是目标密码,则直接返回步数
if(cur == target)
return step;
// 访问所有相邻密码(4 个位置的数字分别加一减一,共 8 种可能)
for(int j = 0; j < cur.size(); ++j)
{
string next1 = cur;
next1[j] = cur[j] == '0' ? '9' : cur[j] - 1; // 加一
if(deadMap.find(next1) == deadMap.end() && visitedMap.find(next1) == visitedMap.end())
{
visitedMap.insert(next1);
q.push(next1);
}
string next2 = cur;
next2[j] = cur[j] == '9' ? '0' : cur[j] + 1; // 减一
if(deadMap.find(next2) == deadMap.end() && visitedMap.find(next2) == visitedMap.end())
{
visitedMap.insert(next2);
q.push(next2);
}
}
}
++step; // 增加步数(层数)
}
return -1;
}
};
复杂度分析:
- 时间复杂度
:4 位转盘锁,每位有 10 种数字,因此 4 位转盘数字的可能性共
种。对于每种可能,需要 O(4) 的时间复杂度遍历每一位去转动,对于转动后的数字,需要 O(4) 的时间复杂度存入队列
- 空间复杂度
:队列最多存储共
个长尾 4 的字符串
执行结果:
执行结果:通过
执行用时:156 ms, 在所有 C++ 提交中击败了 61.82% 的用户
内存消耗:33.8 MB, 在所有 C++ 提交中击败了 65.41% 的用户