首先介绍一下题目,网址如右:https://leetcode-cn.com/problems/open-the-lock/
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
为什么会想到利用BFS,即广度优先搜索解决?首先分析题目,密码锁每处于一个状态时,下一次都有八种可能的状态去转换(每一位都有两种变化),可以将每一个字符串状态看成是树的一个节点,下面都连接着八个后继节点。
那么好,问题转化成树了,紧接着就会想到深度优先搜索(DFS)和广度优先搜索(BFS),为什么用BFS呢,因为题目中求得是“最小旋转次数”,一旦涉及到最小的问题都应该想到用BFS求解。因为对于树来说,BFS是一层一层的去遍历,一旦在某一层找到结果就会直接返回,而DFS是一次搜索完一个分支,最终搜索完所有分支才得到结果,所以只要最优解不在树的最后一层,那么BFS所用的时间就会比DFS更少(因为访问的节点数更少).
好了,既然解法有了,应该使用BFS,具体应该怎么做呢?
BFS存储的数据结构最好用队列实现,队列里每次存储的是一层的树节点,访问完一层就将其全部推出继续加入下一层。一般来说,这类BFS求解的题目都有一个模板,大致如下:
int solution(){ Queue<XX> qu=new LinkedList<>(); qu.offer(初始XX); //初始值输入 visited[];//存储已经访问过的状态,按照情况选择布尔数组或者set集合存储 int res=0; //初始化结果 while(!qu.isEmpty()){ int sz=qu.size(); for(int i=0;i<sz;i++){ XX xx=qu.poll(); 访问xx的后继状态,如果visited包含它或者有其他限制,则continue; 否则将其加入qu; 同时加入visited中; } res++; //这个时候res自增 } return res; }
模板已经有了,稍微思考一下就能得到下面的解法:
public int openLock(String[] deadends, String target) { Queue<String> qu=new LinkedList<>(); qu.offer("0000");//初始状态入队 Set<String> vis=new HashSet<>();//存储已经到达过的状态 Set<String> dead=new HashSet<>(); //存储死亡状态 for(String s:deadends) dead.add(s); int res=0; //存储步数 while(!qu.isEmpty()){ int sz=qu.size(); for(int i=0;i<sz;i++){ String ss=qu.poll(); if(dead.contains(ss)) continue; if(ss.equals(target)) return res; for(int j=0;j<4;j++){ //关于后继状态如何增加,不同的题目不一样,要学会转化条件 String s1=upper(ss,j); String s2=down(ss,j); if(!vis.contains(s1)){ vis.add(s1); qu.offer(s1); } if(!vis.contains(s2)){ vis.add(s2); qu.offer(s2); } } } res++; } return -1; } //转盘锁顺时针旋转 String upper(String s,int idx){ char[] c=s.toCharArray(); //注意如何完成String与char数组的互相转化,以后可能还会用到 if(c[idx]=='9') c[idx]='0'; else c[idx]++; return new String(c); } //转盘锁逆时针旋转 String down(String s,int idx){ char[] c=s.toCharArray(); if(c[idx]=='0') c[idx]='9'; else c[idx]--; return new String(c); }
该解法确实通过了Leetcode所有案例,虽然或许时间复杂度不是最优,但是应该是最为普遍,最广为适用的解法了。
以上就是我对于这一题的解法,如有不足请在评论区批评指正。