广度优先搜索,打开转盘锁

打开转盘锁(Python)

解题思路

单向搜索的思路,即从“0000”出发,遍历它的邻近的组合,不是目标的,加入到已经访问过的队列到中,并加入到队列中,等待下一次循环开始,后领扣时间超时。改用双向搜索,从目标和“0000”两边一起出发,哪边的当前层集合的待检测密码锁数目少,就检测哪一边。当其中有一个集合为0表示断路,不可能接通。当其中一个集合中的邻居包含在另外一个集合当中则表示连接成功。

单向搜索形式

LeetCode-Python-752. 打开转盘锁

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

你可能感兴趣的:(算法实现,算法)