我们可以将 0000 到 9999 这 10000 状态看成图上的 10000 个节点,两个节点之间存在一条边,当且仅当这两个节点对应的状态只有 1 位不同,且不同的那位相差 1(包括 0 和 9 也相差 1 的情况),并且这两个节点均不在数组 deadends 中。那么最终的答案即为 0000 到 target 的最短路径。
我们用广度优先搜索来找到最短路径,从 0000 开始搜索。对于每一个状态,它可以扩展到最多 8 个状态,即将它的第 i = 0, 1, 2, 3 位增加 1 或减少 1,将这些状态中没有搜索过并且不在 deadends 中的状态全部加入到队列中,并继续进行搜索。注意 0000 本身有可能也在 deadends 中。
破解密码的问题就像是一个树搜索的问题,把每一种一步变换的结果都放在该初状态的子节点里
(用deadends和visited进行剪枝,deadends用于去掉包含死路的解法,visited用于避免已经走过的路)
然后再去搜索这个target,搜索的深度就是最短的步数
可以用BFS来实现
class Solution { public: int openLock(vector<string>& deadends, string target) { int step=0; unordered_set<string> deadset(deadends.begin(),deadends.end()); queue<string>tree; tree.push("0000"); unordered_set<string> visited; visited.insert("0000"); if(deadset.find("0000")!=deadset.end()) return -1; while(!tree.empty()) { int n=tree.size(); for(int i=0;i<n;i++) { string cur=tree.front(); tree.pop(); if(cur==target) return step; //位上加一 for(int j=0;j<4;j++) { stringstream ss; int cur_num; string candidate1=cur; ss<<candidate1[j]; ss>>cur_num; cur_num=(cur_num+1)%10; ss.clear();//清空输入输出流 ss<<cur_num; ss>>candidate1[j]; ss.clear(); if(deadset.find(candidate1)==deadset.end() && visited.find(candidate1)==visited.end()) { tree.push(candidate1); visited.insert(candidate1); } } //位上减一 for(int j=0;j<4;j++) { stringstream ss_1; int cur_num_1; string candidate2=cur; ss_1<<candidate2[j]; ss_1>>cur_num_1; cur_num_1-=1; if(cur_num_1<0) cur_num_1+=10; ss_1.clear(); ss_1<<cur_num_1; ss_1>>candidate2; ss_1.clear(); if(deadset.find(candidate2)==deadset.end() && visited.find(candidate2)==visited.end()) { tree.push(candidate2); visited.insert(candidate2); } } } step++; } return -1; } };