打开转盘锁(leetcode)BFS解法
问题描述:
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
如图所示,每次拨动一位密码,同一位置上往前拨或者是往后拨,这样每种密码就有八种拨动方式(我们假设可以密码可以重复拨动的情况下),实际上这样就形成了一个树,它每个节点有八个孩子节点,所以是个八叉树。但密码是不能重复拨动的,即以前拨过的密码不能再拨,这样会死循环,我们要把以前拨过的密码记录下来(我们设一个合集visited),每次拨动新密码时都要在visited中检查,没有的话这个密码才有效。我们只要记录找到的元素在树的第几层即可。
BFS模板:我们的代码与模板代码基本一致
/** * Return the length of the shortest path between root and target node. */
int BFS(Node root, Node target) {
Queue<Node> queue; // store all nodes which are waiting to be processed
int step = 0; // number of steps neeeded from root to current node
// initialize
add root to queue;
// BFS
while (queue is not empty) {
step = step + 1;
// iterate the nodes which are already in the queue
int size = queue.size();
for (int i = 0; i < size; ++i) {
Node cur = the first node in queue;
return step if cur is target;
for (Node next : the neighbors of cur) {
add next to queue;
}
remove the first node from queue;
}
}
return -1; // there is no path from root to target
}
——————————AC代码——————————
速度很慢,更高效的代码以后发
class Solution {
public:
int openLock( vector<string> &deadends, string target ) {
//这里visited要用set容器,原因有二
//原因1:set容器find函数查找速度快,不易时间超限,他按二叉树存储,且插入时自动排序
//原因2:set容器自带查找函数find,不用我们自己再写
set<string> visited; //存储已经访问过的密码和死亡数字deadends
for( int i = 0; i < deadends.size(); i++ ) {
visited.insert( deadends[i] ); //把deadends存进visited
}
//find函数找到的话返回元素的迭代器,未找到的话返回end
set<string>::iterator pos = visited.find( "0000" );
if( pos != visited.end() ) {
return -1; //说明元素已经访问过了,直接return
}
visited.insert( "0000" );
queue<string> q;
int count = 0; //记录树的层数
q.push( "0000" );
//参考模板
while( !q.empty() ) {
int size = q.size();
for( int i = 0; i < size; i++ ) {
string front = q.front(); //队列的头元素
q.pop();
if( front == target ) {
return count; //找到了目标元素,返回count
}
//j从0到3,代表4位密码的0-3位,每位上都有加减两种操作
for( int j = 0; j < 4; j++ ) {
//addTemp 表示执行了加操作的密码
string addTemp = codeAdd( front, j );
set<string>::iterator s = visited.find( addTemp );
//此密码以前未访问过
if( s == visited.end() ) {
q.push( addTemp ); //入队
visited.insert( addTemp );
}
string subtractTemp = codeSubstact( front, j );
s = visited.find( subtractTemp );
if( s == visited.end() ) {
q.push( subtractTemp );
visited.insert( subtractTemp );
}
}
}
count++; //搜索玩一次,树的层次加1
}
return -1; //如果能找到的话肯定已经返回了,到不了这一步,到这肯定没找到,返回-1
}
//向上拨动密码,执行类似加的操作
string codeAdd( string s, int i ) {
//当密码位是9时,再往上就是0
if( s[i] == '9' ) {
s[i] -= 9; //等同于s[i]='0'
} else {
s[i] += 1;
}
return s;
}
//向下拨动密码,执行类似减的操作
string codeSubstact( string s, int i ) {
//当密码位是0时,再往下就是9
if( s[i] == '0' ) {
s[i] += 9; //等同于s[i]='9'
} else {
s[i] -= 1;
}
return s;
}
};
我自己做的时候思路挺乱挺麻烦的,因为有加减操作,我把string转成了int,查找是设置了一个数组,要不停处理下表与元素的关系。visited用的vector容器,不仅要自己编写查找函数,而且查找速度特别特别满,总是时间超弦。
总结:认真学习BFS模板