打开转盘锁(leetcode)BFS解法

打开转盘锁(leetcode)BFS解法

*力扣链接
*图片来源
*模板代码来源

问题描述:

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。

《打开转盘锁(leetcode)BFS解法》

如图所示,每次拨动一位密码,同一位置上往前拨或者是往后拨,这样每种密码就有八种拨动方式(我们假设可以密码可以重复拨动的情况下),实际上这样就形成了一个树,它每个节点有八个孩子节点,所以是个八叉树。但密码是不能重复拨动的,即以前拨过的密码不能再拨,这样会死循环,我们要把以前拨过的密码记录下来(我们设一个合集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模板

    原文作者:x-robot
    原文地址: https://blog.csdn.net/elexiangchirou/article/details/121113500
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞