题目:打开转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/queue-stack/kj48j/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Solve:
BFS广度优先搜索、双向BFS均能解答
(2020.12.17)单向BFS会超时
解答要点:
1.以基本的BFS框架为基础
2.队列、集合数据结构的使用时机
3.单向bfs和双向bfs,达到目标的判断条件
4.何时扩展、如何一步步扩展=>的具体实现
//单向BFS
Queue<string> q = new Queue<string>();//存放5个number,密码和1个状态位
Queue<string> visited = new Queue<string>();//存放4个number,即密码
while (q.Count > 0)
{
int sz = q.Count;
for (int i = 0; i < sz; i++)
{
string cur;//当前密码
//char[] list_temp = list.Clone() as char[];
cur = advance(q.Dequeue()).Remove(4, 1);
if (cur == "0000")
{
return step;
}
if (deadends.Contains(cur) || visited.Contains(cur))
{
continue;
}
visited.Enqueue(cur);
for (int j = 1; j < 9; j++)
{
q.Enqueue(cur+j.ToString());
}
}
step++;
}
//双向BFS,此方法有待改进
//头部bfs
Queue<int> head_q = new Queue<int>();//存放当前的number
Queue<int> head_visited = new Queue<int>();//存放访问过的number
Queue<int> head_s = new Queue<int>();//存放待调整的状态参数
//尾部bfs
Queue<int> tail_q = new Queue<int>();//存放当前的number
Queue<int> tail_visited = new Queue<int>();//存放访问过的number
Queue<int> tail_s = new Queue<int>();//存放待调整的状态参数
head_q.Enqueue(0);
head_s.Enqueue(0);
tail_q.Enqueue(m_target);
tail_s.Enqueue(0);
int h_step = -1;
int t_step = -1;
while (head_s.Count > 0&&tail_s.Count>0)
{
h_step++;
int h_sz = head_s.Count;
for (int j = 0; j < h_sz; j++)
{
int head = advance(head_q.Dequeue(), head_s.Dequeue());
if (tail_visited.Contains(head))
{
step = h_step + t_step;
return step;
}
if ((!locked.Contains(head))&&(!head_visited.Contains(head)))
{
//将该节点的子节点放入队列
head_visited.Enqueue(head);
for (int i = 1; i < 5; i++)
{
head_q.Enqueue(head);
head_s.Enqueue(i);
head_q.Enqueue(head);
head_s.Enqueue(-i);
}
}
}
t_step++;
int t_sz = tail_s.Count;
for (int j = 0; j < t_sz; j++)
{
int tail = advance(tail_q.Dequeue(), tail_s.Dequeue());
if (head_visited.Contains(tail))
{
step = h_step + t_step;
return step;
}
if ((!locked.Contains(tail)) && (!tail_visited.Contains(tail)))
{
//将该节点的子节点放入队列
tail_visited.Enqueue(tail);
for (int i = 1; i < 5; i++)
{
tail_q.Enqueue(tail);
tail_s.Enqueue(i);
tail_q.Enqueue(tail);
tail_s.Enqueue(-i);
}
}
}
}
需要改进的点:(2020.12.17)
- 不转数字,直接字符串与字符
- 用集合代替队列
- deadkey直接初始化到visited里
- 2个bfs使用轮换方法,轮流与对边比较、自我扩散(每次都只于最外侧比较)