如题:
-
分析题目:
-
题目用长度为
4
的字符串表示密码,我们转动密码的时候每一位上只能上或下转动一次,我们可以理解转动一位密码就相当于更改这个密码字符串其中一个字符的值,因此我们可以编写两个方法,分别代表将密码字符串中的某一位向上移动一位和向下移动一位://将密码字符串s的第t位向上拨动一位 public String moveUp(String s,int t){ char[] chars = s.toCharArray();//将当前密码字符串转成char数组,便于更改每一位的值,最后将这个char数组转成字符串 if (chars[t]=='9'){//当值为9时向上拨动应该为0 chars[t]='0'; }else { chars[t] += 1;//其它情况下就是当前字符值+1 //注意这里是加int类型的1而不是char类型的1,因为char类型底层是对应ASCII的,ASCII码是用int类型的值对应查找char类型的字符, //而'1','2'这些字符在码表中是连续的,我们将'1'的ASCII码值+1可以得到'2', //但是如果加的是字符类型的'1'那么它的ASCII码值并不是1,此时得出的字符并不是'2' } return new String(chars); } //同理 public String moveDown(String s,int t){ char[] chars = s.toCharArray(); if (chars[t]=='0'){ chars[t]='9'; }else { chars[t] -= 1; } return new String(chars); }
-
-
思路:
-
我们要从当前
'0000'
开始找到正确的密码,那么就需要穷举所有的可能来判断是否和目标target值相等。 -
如何穷举?
- 当我们在转动密码的时候我们可以转动四位中的任何一位,而每次转动有两个方向,因此我们每次转动后有8中可能出现的结果。我们可以理解成从当前状态做出一个操作后变到下一个状态,将这两个状态用线连接起来表示他们是相邻的两个状态,因此第一个状态后会有
8
根线连着代表接下来有8
个可能状态。然后我们再从这8个状态中依次进行相同的操作,此时我们可以发现:这很像无向图的遍历。- 因此我们可以将穷举所有可能抽象理解为遍历一个无向图!!!
- 当我们在转动密码的时候我们可以转动四位中的任何一位,而每次转动有两个方向,因此我们每次转动后有8中可能出现的结果。我们可以理解成从当前状态做出一个操作后变到下一个状态,将这两个状态用线连接起来表示他们是相邻的两个状态,因此第一个状态后会有
-
图的遍历有两种方式:
DFS
遍历BFS
层次遍历
- 这里我们应该使用
BFS
遍历的方法,因为我们需要计算从'0000'
至少转动多少下能够找到正确密码,而**BFS
层次遍历的时候每一层就相当于转动了一次,最终找到正确密码的时候我们只需计算它当前在遍历的第几层,这就是找到它的最短路径**!
-
题目中额外有一个
deadends
数组,我们转动密码的时候要注意不能转动成这个状态,否则密码会被锁定,因此我们可以用一个HashSet
来保存这个数组的值,当我们遍历到一个新状态的时候就判断这个状态是否在这个HashSet
集合中,如果是就终止这个状态再往下遍历;如果不是就在这个状态下继续遍历。
-
-
代码:
class Solution { public int openLock(String[] deadends, String target) { Queue<String> q = new LinkedList<>();//BFS层次遍历需要用到的队列 HashSet<String> deadendSet = new HashSet<>(Arrays.asList(deadends));//保存会导致锁定的密码值 HashSet<String> visited = new HashSet<>();//保存哪些状态我们遍历过,即哪些密码值我们有转到过,如果遍历过就不加入队列中 String start = "0000"; q.offer(start);//往队列中加入初始值 visited.add(start);//将初始值设置为遍历过的状态 int stepCount=0;//用来记录找到正确密码的最少步数 while (!q.isEmpty()){//队列非空时一直遍历 int size = q.size();//记录当前层有多少个结点,遍历完这些结点后遍历下一层 for (int j = 0; j < size; j++) { String s = q.poll();//队头元素出队 if (deadendSet.contains(s))continue;//如果当前状态会导致锁定,就跳过当前此次循环,即停止从该层向下遍历 if (target.equals(s))return stepCount;//如果找到了目标,就返回当前记录的步数 for (int i = 0; i < 4; i++) {//这个密码四个位置每个位置都可以转动,一次遍历 String up = moveUp(s, i);//先向上转动 if (!visited.contains(up)){//如果向上转动后的值我们未曾遍历过,就加入队列,否则不加入 q.offer(up); visited.add(up); } String down = moveDown(s,i);//再向下转动 if (!visited.contains(down) ){//如果向下转动后的值我们未曾遍历过,就加入队列,否则不加入 q.offer(down); visited.add(down); } } } stepCount++;//遍历完当前层,层次+1,代表当前层次 } return -1; } public String moveUp(String s,int t){ char[] chars = s.toCharArray(); if (chars[t]=='9'){ chars[t]='0'; }else { chars[t] += 1; } return new String(chars); } public String moveDown(String s,int t){ char[] chars = s.toCharArray(); if (chars[t]=='0'){ chars[t]='9'; }else { chars[t] -= 1; } return new String(chars); } }