打开转盘锁(Python)
解题思路
单向搜索的思路,即从“0000”出发,遍历它的邻近的组合,不是目标的,加入到已经访问过的队列到中,并加入到队列中,等待下一次循环开始,后领扣时间超时。改用双向搜索,从目标和“0000”两边一起出发,哪边的当前层集合的待检测密码锁数目少,就检测哪一边。当其中有一个集合为0表示断路,不可能接通。当其中一个集合中的邻居包含在另外一个集合当中则表示连接成功。
单向搜索形式
from collections import deque
class Solution(object):
def openLock(self, deadends, target):
"""
:type deadends: List[str]
:type target: str
:rtype: int
"""
deadends = set(deadends)
if "0000" in deadends: # 如果连起点都不能走就88
return -1
queue = deque()
queue.append(["0000", 0])
cnt = 0
while queue:
node, cnt = queue.popleft() # 取一个点出来,cnt是当前走的步数
if node == target: # 找到了
return cnt
for i in range(4):
for j in [1, -1]:
next_node = node[:i] + str((int(node[i]) + j) % 10) + node[i + 1:]
if next_node not in deadends: # 新的点可以走而且没走过
deadends.add(next_node) # 避免重复
queue.append([next_node, cnt + 1])
return -1
双向搜索形式
这个问题是单个源点(“0000”)单个目标(输入的target)的广度优先搜索。我们可以想象当我们到达目标节点的时候,我们同时还遍历了求解树中位于同一层的其他节点。尽管在那一层中我们只需要遍历一个节点,我们却实际上遍历很多不必要的节点,因此单向搜索是存在优化空间的。
我们可以把单项搜索改为双向搜索,也就是既从源点出发向着目标搜索,也从目标出发向着源点搜索。如果两个方向搜索最终能够在中间某个位置相遇,那么表明存在从源点到目标的路径。
from collections import deque
class Solution(object):
def openLock(self, deadends, target):
'''
:type deadends: List[str]
:type target: str
:rtype: int
'''
self.deadends = set(deadends)
if '0000' in deadends or target in deadends: # 起点或终点为死亡数据是返回-1
return -1
if target == '0000':
return 0
queue_order = deque()
queue_inorder = deque()
queue_order.append('0000')
queue_inorder.append(target)
step_order=0
step_inorder=0
if self.queuesArrived(queue_order, queue_inorder):
return step_order + step_inorder+1
while queue_order and queue_inorder: # 两边同时搜索
queue_order = self.queueStepAdd(queue_order) # 左边提升一
step_order=step_order+1
if self.queuesArrived(queue_order, queue_inorder):
return step_order + step_inorder + 1
queue_inorder = self.queueStepAdd(queue_inorder) # 右边提升一
step_inorder = step_inorder + 1
if self.queuesArrived(queue_order, queue_inorder):
return step_order + step_inorder + 1
return -1
def queueStepAdd(self, queue):
if len(queue)==0:
return None
length=len(queue)
for index in range(length):
node = queue.popleft() # 取一个点出来
for i in range(4):
for j in [1, -1]:
next_node = node[:i] + str((int(node[i]) + j) % 10) + node[i + 1:]
if next_node not in self.deadends: # 新的点可以走而且没走过
self.deadends.add(next_node) # 避免重复
queue.append(next_node)
return queue
def queuesArrived(self,queue1,queue2): # 两队列是否相交
length = len(queue1)
for index in range(length):
node = queue1[index] # 取一个点出来
for i in range(4):
for j in [1, -1]:
next_node = node[:i] + str((int(node[i]) + j) % 10) + node[i + 1:]
if next_node in set(queue2):
return True
return False