管科研一新生首刷leetcode,本题思路来源leetcode官解,写此帖初衷是为了帮助自己更好的理解和“背诵”代码,方便后期复习,希望各位大佬多多指教!
题目描述
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。使用示例
输入:deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
输出:6
解释:
可能的移动序列为 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。
注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 这样的序列是不能解锁的,
因为当拨动到 “0102” 时这个锁就会被锁定。
解题思路
创建一个队列放入需要搜索的数字以及当前的搜索次数,再创建一个seen列表(set)存放已经访问过的数字,防止进行重复访问。
首先将“0000”以及times=0加入队列,进行广度优先搜索;先判断“0000”是否在deadends 中,若存在则直接返回-1;若不在,则对“0000”的各个位数分别进行一次拨动,例:“0001”,“0009”,“0010”,“0090”,“0100”,“0900”,“1000”,“9000”。依次判断上述数字是否在seen以及deadends中,若都不存在则加入队列,同时加入当前的times(每旋转一次times加一,在代码里就是一次for loop,但是不知道文字怎么表述);继续第二次旋转后的列表搜索,以此类推,若搜索过程中,某个变换的字符等于target,则返回当前times,当搜索完成未找到相应字符时,返回-1。
python代码实现
class Solution:
def openLock(self, deadends: List[str], target: str) -> int:
if target == "0000":
return 0
dead = set(deadends)
if "0000" in dead:
return -1
def prev(n):
return '9' if n == '0' else str(int(n) - 1)
def succ(n):
return '0' if n == '9' else str(int(n) + 1)
def get(status):
s = list(status)
for i in range(4):
num = s[i]
s[i] = prev(num)
yield "".join(s)
s[i] = succ(num)
yield "".join(s)
s[i] = num # 你这步是要让s继续变成[0,0,0,0]没有这个你i = 1 进行loop的时候就是[1,0,0,0]了,但是每次只能移动一个的嘛!
q = collections.deque([('0000',0)])
seen = {'0000'}
while q:
status,step = q.popleft()
for next_status in get(status):
if next_status not in seen and next_status not in dead:
if next_status == target:
return step + 1
q.append((next_status,step+1))
seen.add(next_status)
return -1
yield生成器的使用方法
- yield只能在函数中使用,其作用与return相似?
- yield返回的生成器使用for loop或者next()对元素进行遍历
参考资料:
https://zhuanlan.zhihu.com/p/268605982