你有一个带有四个圆形拨轮的转盘锁。每一个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
。每一个拨轮能够自由旋转:例如把 '9'
变为 '0'
,'0'
变为 '9'
。每次旋转都只能旋转一个拨轮的一位数字。java
锁的初始数字为 '0000'
,一个表明四个拨轮的数字的字符串。python
列表 deadends
包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,没法再被旋转。git
字符串 target
表明能够解锁的数字,你须要给出最小的旋转次数,若是不管如何不能解锁,返回 -1。数组
You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
. The wheels can rotate freely and wrap around: for example we can turn '9'
to be '0'
, or '0'
to be '9'
. Each move consists of turning one wheel one slot.app
The lock initially starts at '0000'
, a string representing the state of the 4 wheels.学习
You are given a list of deadends
dead ends, meaning if the lock displays any of these codes, the wheels of the lock will stop turning and you will be unable to open it.ui
Given a target
representing the value of the wheels that will unlock the lock, return the minimum total number of turns required to open the lock, or -1 if it is impossible.spa
示例 1:code
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202" 输出:6 解释: 可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。 注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的, 由于当拨动到 "0102" 时这个锁就会被锁定。
示例 2:队列
输入: deadends = ["8888"], target = "0009" 输出:1 解释: 把最后一位反向旋转一次便可 "0000" -> "0009"。
示例 3:
输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888" 输出:-1 解释: 没法旋转到目标数字且不被锁定。
示例 4:
输入: deadends = ["0000"], target = "8888" 输出:-1
提示:
deadends
的长度范围为 [1, 500]
。target
不会在 deadends
之中。deadends
和 target
中的字符串的数字会在 10,000 个可能的状况 '0000'
到 '9999'
中产生。Note:
deadends
will be in the range [1, 500]
.target
will not be in the list deadends
.deadends
and the string target
will be a string of 4 digits from the 10,000 possibilities '0000'
to '9999'
.题目要求给出最小的旋转次数,应该就是用 BFS(广度优先搜索)解题了。初始字符串为 "0000"
那么第二步就是 "1000" "9000" "0100" "0900" "0010" "0090" "0001" "0009"
共八个字符串,也就是说每一步的字符串拨动密码扩展到下一步时能够获得八个新字符串。把它想象成图的形式:很明显至关于每一个节点后有八个节点,用 BFS 每次走一个节点,直到达到目标节点,便是最短路径。
另外须要注意:每次到要判断节点是否为给出的死亡数字,而且把已遍历的节点也加入死亡数字以防止重复。这样只能将原数组形式的死亡数字转为哈希表以减小查找操做的复杂度。用队列暂存下一步须要遍历的节点。Java、python没法直接修改字符串里的字符.Java可先转换成 char 型数组,python可借助切片组装新字符串。
Java:
class Solution { public int openLock(String[] deadends, String target) { HashSet<String> dead_set = new HashSet<>(Arrays.asList(deadends));//死亡数字转为哈希表 if (dead_set.contains("0000")) return -1;//死亡数字若是含有初始节点,返回-1 Queue<String> queue = new LinkedList<>();//队列 queue.add("0000");//加入初始节点 int count = 0;//记录步数 while (!queue.isEmpty()) {//节点未访问完,队列内的节点不为空 int size = queue.size();//每一步节点数 while (size-- > 0) { String tmp = queue.remove();//弹出头节点 if (target.equals(tmp)) return count;//若是与目标数相同,直接返回步数 char[] c = tmp.toCharArray();//转为数组 for (int j = 0; j < 4; j++) {//每次修改四位数字的一位 int i = c[j] - '0';//转为int型 c[j] = (char) ('0' + (i + 9) % 10);//数字-1。余数运算可防止节点为0、9时出现-一、10的状况 String s = new String(c);//获得新字符串 if (!dead_set.contains(s)) {//字符串不在死亡数字中时 queue.add(s);//添加到队列做为下一步须要遍历的节点 dead_set.add(s);//下一步必访问该节点,因此可先加入到死亡数字 } c[j] = (char) ('0' + (i + 11) % 10);//数字+1 s = new String(c); if (!dead_set.contains(s)) { queue.add(s); dead_set.add(s); } c[j] = (char) ('0' + i); } } count++; } return -1; } }
Python:
class Solution: def openLock(self, deadends: List[str], target: str) -> int: #转成哈希表 dead_set = set(deadends) if '0000' in dead_set: return -1 que = collections.deque(['0000']) count = 0 while que: for x in range(len(que)): #从左取出头节点 tmp = que.popleft() if tmp == target: return count for i in range(4): #分别存修改字符的左半部分字符串,待修改字符(转成int),右半部分字符 left, mid, right = tmp[:i], int(tmp[i]), tmp[i + 1:] #j数字加1、减一拨动操做 for x in [-1, 1]: s = left + str((mid + x) % 10) + right#切片拼接字符 if not s in dead_set: dead_set.add(s) que.append(s) count += 1 return -1
关注微.信.公.众号:爱写Bug,一块儿学习吖