打开转盘锁

        你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。

        锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。

        列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。

        字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。

示例 1:

输入: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
解释:无法旋转到目标数字且不被锁定。

提示:

  • 1 <= deadends.length <= 500
  • deadends[i].length == 4
  • target.length == 4
  • target 不在 deadends 之中
  • target 和 deadends[i] 仅由若干位数字组成

示例代码: 【广度优先搜索BFS】

from collections import deque


class Solution(object):
    def openLock(self, deadends, target):
        if target == "0000":
            return 0
        dead = set(deadends)
        if "0000" in dead:
            return -1

        def num_pre(x):
            return "9" if x == "0" else str(int(x) - 1)

        def num_post(x):
            return "0" if x == "9" else str(int(x) + 1)

        # 枚举 status 通过一次旋转得到的数字
        def generator_num(status):
            s = list(status)
            for i in range(4):
                num = s[i]
                s[i] = num_pre(num)
                yield ''.join(s)
                s[i] = num_post(num)
                yield ''.join(s)
                s[i] = num

        quene = deque([("0000", 0)])
        tmp = {"0000"}
        while quene:
            status, step = quene.popleft()
            for next_status in generator_num(status):
                if next_status not in tmp and next_status not in dead:
                    if next_status == target:
                        return step + 1
                    quene.append((next_status, step + 1))
                    tmp.add(next_status)
        return -1


deadends = ["0201", "0101", "0102", "1212", "2002"]
target = "0202"
obj = Solution()
ans = obj.openLock(deadends, target)
print(ans)

思路解析:

        使用BFS,找出从初始数字 0000 到解锁数字 target 的最小旋转次数。

        具体地,一开始将 (0000, 0)加入队列,并使用该队列进行广度优先搜索。在搜索的过程中,设当前搜索到的数字为 status,旋转的次数为step,我们可以枚举 status 通过一次旋转得到的数字。设其中的某个数字为 next_status,如果其没有被搜索过,我们就将 (next_status,step+1) 加入队列。如果搜索到了target,我们就返回其对应的旋转次数。

        为了避免搜索到死亡数字,使用哈希表存储deadends 中的所有元素,这样在搜索的过程中,我们可以均摊 O(1)地判断一个数字是否为死亡数字。同时,还需要一个哈希表存储所有搜索到的状态,避免重复搜索。

        如果搜索完成后,仍没有搜索到 target,说明我们无法解锁,返回 -1。

时间复杂度:

示例代码2:

from collections import deque


class Solution(object):
    def openLock(self, deadends, target):
        # 如果target是'0000'则不需要拨动 返回0
        if target == "0000":
            return 0
        dead = set(deadends)
        if "0000" in dead:
            return -1

        def num_up(x, i):
            lst = list(x)
            if lst[i] == "9":
                lst[i] = "0"
            else:
                lst[i] = str(int(lst[i]) + 1)
            return "".join(lst)

        def num_down(x, i):
            lst = list(x)
            if lst[i] == "0":
                lst[i] = "9"
            else:
                lst[i] = str(int(lst[i]) - 1)
            return "".join(lst)

        step = 0  # 拨动次数 初始时没拨所以次数为0
        queue = deque()
        visited = set()  # 存放已经比较过的字符组合、死亡数字组合 避免死循环
        visited.update(deadends)  # 初始化为死亡数字组合

        queue.append("0000")

        while queue:
            length = len(queue)  # 按层处理队列中的字符串
            for i in range(length):
                cur = queue.popleft()  # 当前处理的节点
                if cur in visited:  # 当前节点已经处理过或属于死亡数字组合 所以跳过
                    continue
                else:
                    visited.add(cur)  # 首次比较 加入已经处理的集合
                # 判断是否满足终止条件
                if cur == target:
                    return step
                # 不满足终止条件将所有相邻的节点加入队列
                for j in range(4):  # cur的每位数都可以上拨或下拨 这些都是cur的相邻节点
                    up = num_up(cur, j)
                    # 需要判断上拨、下拨后的组合是否合法(在不在visited里) 不合法就跳过
                    if up not in visited:
                        queue.append(up)
                    down = num_down(cur, j)
                    if down not in visited:
                        queue.append(down)
            # 每循环一遍是一次拨动 次数要+1
            step += 1
        # 所有可能都穷举后无法达到target说明不能解锁 返回-1
        return -1


deadends = ["0201", "0101", "0102", "1212", "2002"]
target = "0202"
obj = Solution()
ans = obj.openLock(deadends, target)
print(ans)

 思路解析:

        密码锁可以看作是一层一层的,因为在穷举可能的密码组合时,每一位每次有2种(上拨、下拨)情况,依据每种情况又可以再次延伸……这种特性可以采用BFS思想求解,想象一下按层处理每种密码组合:

        将起点加入队列q,只要q不为空,循环处理当前层的节点(循环条件是进入循环时q的长度),若当前节点cur是终点(target)则结束循环返回处理次数(层数)step,否则将cur的邻接节点(下一层的节点)加入q,一轮循环处理结束step++。当队列q为空(全部节点都处理完后)说明没有满足结束条件的节点,即不存在解,返回-1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过 HTML、CSS 和 JavaScript 实现转盘效果。下面是一个简单的 HTML 转盘实现示例: ```html <!DOCTYPE html> <html> <head> <title>HTML 转盘示例</title> <style> /* 定义转盘样式 */ .wheel { width: 300px; height: 300px; margin: 50px auto; position: relative; overflow: hidden; border-radius: 50%; box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5); } .wheel:before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to right, #fff 50%, #f00 50%); transform-origin: 0 50%; transform: rotate(22.5deg); transition: transform 5s; } </style> </head> <body> <div class="wheel"></div> <script> // 定义转盘旋转函数 function spinWheel() { // 随机生成旋转角度 var deg = Math.floor(Math.random() * 360) + 3600; // 获取转盘元素 var wheel = document.querySelector(".wheel"); // 设置转盘旋转动画 wheel.style.transition = "transform 10s ease-out"; wheel.style.transform = "rotate(" + deg + "deg)"; // 监听旋转动画结束事件 wheel.addEventListener("transitionend", function() { // 移除旋转动画 wheel.style.transition = null; // 计算选中的扇形 var selected = Math.floor(((360 - deg % 360) % 360) / 45); // 弹出选中的扇形编号 alert(selected); }); } // 绑定转盘点击事件 document.querySelector(".wheel").addEventListener("click", function() { // 禁用转盘点击事件 this.removeEventListener("click", arguments.callee); // 开始转盘旋转 spinWheel(); }); </script> </body> </html> ``` 该示例中,通过定义 `.wheel` 样式来设置转盘的基本样式,然后使用 `:before` 伪元素来绘制扇形。在 JavaScript 中,定义了 `spinWheel()` 函数来实现转盘旋转效果,并在转盘点击事件中调用该函数来触发旋转动画。旋转结束后,通过计算旋转角度来确定选中的扇形,弹出选中扇形的编号。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值