【算法】BFS—解开密码锁的最少次数

news2024/9/19 5:24:38

题目

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

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

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

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

解题

        第一步,我们不管所有的限制条件,不管 deadends 和 target 的限制,就思考一个问题:如果让你设计一个算法,穷举所有可能的密码组合,你将怎么做?

        总共有4个位置,每个位置可以向上转,也可以向下转,也就是有8种可能。比如,从 '0000' 开始,转一次,可以穷举出 '1000' ,'9000','0100','0900'······共8种密码。然后,再以这8种密码作为基础,对每种密码再转一下,穷举出所有可能······

        仔细想想,这就可以抽象成一幅图,每个节点有8个相邻的节点,求的又是最短距离,这不就是典型的BFS嘛,这时框架就可以派上用场了,先写出一个“简陋”的BFS框架代码:

package BFS;

import java.util.LinkedList;
import java.util.Queue;

// leetcode 109 打开转盘锁
public class OpenTheTurntable {

    public String plusOne(String str, int j) {
        char[] ch = str.toCharArray();
        if (ch[j] == '9') {
            ch[j] = '0';
        } else {
            ch[j] += 1;
        }
        return new String(ch);
    }

    public String minusOne(String str, int j) {
        char[] ch = str.toCharArray();
        if (ch[j] == '0') {
            ch[j] = '9';
        } else {
            ch[j] -= 1;
        }
        return new String(ch);
    }

    // BFS框架伪码,打印所有可能的密码
    public void BFS(String target) {
        Queue<String> queue = new LinkedList<>();
        queue.offer("0000");
        while (!queue.isEmpty()) {
            int sz = queue.size();
            // 将当前队列中的所有节点向周围扩散
            for (int i = 0; i < sz; i++) {
                String cur = queue.poll();
                // 判断是否到达终点
                System.out.println(cur);
                // 将一个节点的相邻节点加入队列
                for (int j = 0; j < 4; j++) {
                    String up = plusOne(cur, j);
                    String down = minusOne(cur, j);
                    queue.offer(up);
                    queue.offer(down);
                }
            }
            // 在这里增加步数
        }
        return;
    }

}

        注意:这段代码当然有很多问题,但是我们做算法题肯定不是一蹴而就的,而是从简陋到完美的。

        这段BFS代码已经能够穷举所有可能的密码组合了,但是显然不能完成题目,还有如下问题需要解决:

        1.会走回头路。比如,从 '0000' 拨到 '1000',但是等从队列中拿出 '1000'时,还会拨出一个 '0000',这样会产生死循环。

        2.没有终止条件,按照题目要求,找到 target 就应该结束并返回拨动的次数。

        3.没有对 deadends的处理,按道理这些“死亡密码”是不能出现的,也就是说遇到这些密码的时候需要跳过,不能进行任何操作。

        只要按照BFS框架在对应的位置稍微修改即可修复这些问题:

package BFS;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

// leetcode 109 打开转盘锁
public class OpenTheTurntable {

    public String plusOne(String str, int j) {
        char[] ch = str.toCharArray();
        if (ch[j] == '9') {
            ch[j] = '0';
        } else {
            ch[j] += 1;
        }
        return new String(ch);
    }

    public String minusOne(String str, int j) {
        char[] ch = str.toCharArray();
        if (ch[j] == '0') {
            ch[j] = '9';
        } else {
            ch[j] -= 1;
        }
        return new String(ch);
    }

    int openLock(String[] deadends, String target) {
        // 记录需要跳过的死亡密码
        Set<String> deads = new HashSet<>();
        for (String s : deadends) {
            deads.add(s);
        }
        // 记录已经穷举过的密码,防止走回头路
        Set<String> visited = new HashSet<>();
        Queue<String> queue = new LinkedList<>();
        // 从起点开始启动广度优先搜索
        int step = 0;
        queue.offer("0000");
        visited.add("0000");
        while (!queue.isEmpty()) {
            int sz = queue.size();
            // 将当前队列中的所有节点向周围扩散
            for (int i = 0; i < sz; i++) {
                String cur = queue.poll();
                // 判断密码是否合法,是否到达终点
                if (deads.contains(cur)) {
                    continue;
                }
                if (cur.equals(target)) {
                    return step;
                }
                // 将一个节点的相邻节点加入队列
                for (int j = 0; j < 4; j++) {
                    String up = plusOne(cur, j);
                    if (!visited.contains(up)) {
                        queue.offer(up);
                        visited.add(up);
                    }
                    String down = minusOne(cur, j);
                    if (!visited.contains(down)) {
                        queue.offer(down);
                        visited.add(down);
                    }
                }
            }
            // 在这里增加步数
            step += 1;
        }
        // 如果穷举完都没有找到目标密码,那就是找不到了
        return -1;
    }

    public static void main(String[] args) {
        OpenTheTurntable openTheTurntable = new OpenTheTurntable();
        String[] deadends = {"8888"};
        int count = openTheTurntable.openLock(deadends, "0008");
        System.out.println(count);
    }

}

        至此,这道题目就解决了。但优化还没有结束,因为终点在哪里是知道的,所以可以用双向BFS进行优化。这里先留白,以后再补充······

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2141093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java重修笔记 第五十七天 坦克大战(七)多线程基础 - 编程练习

1. 线程之间的协调控制&#xff08;通知方式&#xff09; public class Homework04 {public static void main(String[] args) {// 在 main 方法中启动两个线程// 第一个线程内循环打印 1 到 100 以内的整数// 直到第二个线程从键盘读取到 "Q" 指令后结束第一个线程…

Porcupine - 语音关键词唤醒引擎

文章目录 一、关于 Porcupine特点用例尝试一下 语言支持性能 二、Demo1、Python Demo2、iOS DemoBackgroundService DemoForegroundApp Demo 3、网页 Demo3.1 Vanilla JavaScript 和 HTML3.2 Vue Demos 三、SDK - Python 一、关于 Porcupine Porcupine 是一个高度准确和轻量级…

LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路)

LC并联电路在正弦稳态下的传递函数推导&#xff08;LC并联谐振选频电路&#xff09; 本文通过 1.解微分方程、2.阻抗模型两种方法推导 LC 并联选频电路在正弦稳态条件下的传递函数&#xff0c;并通过仿真验证不同频率时 vo(t) 与 vi(t) 的幅值相角的关系。 电路介绍 已知条件…

Axure RP实战:打造高效图形旋转验证码

Axure RP实战&#xff1a;打造高效图形旋转验证码 在数字产品设计的海洋中&#xff0c;验证码环节往往是用户交互体验的细微之处&#xff0c;却承载着验证用户身份的重要任务。 传统的文本验证码虽然简单直接&#xff0c;但随着用户需求的提高和设计趋势的发展&#xff0c;它…

vue2的diff算法

Vue2 的虚拟 DOM diff 算法是一种高效的算法&#xff0c;用于比较新旧两个虚拟 DOM 树&#xff0c;找出差异并更新到真实 DOM 上。这个算法的核心在于尽量减少不必要的 DOM 操作&#xff0c;提高性能。 虚拟dom&#xff1a;把DOM数据化&#xff0c;先通过不断地操作数据&#…

如何在手机端跑大模型?

最近新入手了一台 arm 开发板&#xff0c;内置安装了 Android 13 系统。 昨天把网络问题给解决了&#xff1a;安卓连接 WIFI 但无法上网&#xff1f;盘点踩过的那些坑 今日分享&#xff0c;继续带大家实操&#xff1a;如何把大模型&#xff08;LLM&#xff09;部署到移动端&a…

文章资讯职场话题网站源码整站资源自带2000+数据

介绍&#xff1a; 数据有点多&#xff0c;数据资源包比较大&#xff0c;压缩后还有250m左右。值钱的是数据&#xff0c;网站上传后直接可用&#xff0c;爽飞了 环境&#xff1a;NGINX1.18 mysql5.6 php7.2 代码下载

JUC学习笔记(三)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 八、共享模型之工具--JUC8.1 AQS 原理1. 概述2 实现不可重入锁自定义同步器自定义锁 3.心得起源目标设计1) state 设计2&#xff09;阻塞恢复设计3&#xff09;队列…

学习笔记 韩顺平 零基础30天学会Java(2024.9.16)

P563 自定义泛型方法 当调用方法时&#xff0c;要传入参数&#xff0c;因为当传入参数时&#xff0c;编译器就可以确定泛型代表的类型 泛型方法和方法使用了泛型是不一样的 泛型方法可以使用类声明的泛型&#xff0c;也可以使用自己的泛型 P564 泛型方法练习 P565 泛型的继承和…

Python编码系列—Python适配器模式:无缝集成的桥梁

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

二叉树OJ题——另一棵树的子树

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 另一棵树的子树 题目描述&#xff1a;判断当前树A是否是树B的子树。 二、解题思路 时间复杂度&#xff1a;O(n*m) 三、解题代码

Learn ComputeShader 15 Grass

1.Using Blender to create a single grass clump 首先blender与unity的坐标轴不同&#xff0c;z轴向上&#xff0c;不是y轴 通过小键盘的数字键可以快速切换视图&#xff0c;选中物体以后按下小键盘的点可以将物体聚焦于屏幕中心 首先我们创建一个平面&#xff0c;宽度为0.2…

AI替代插画师跟设计师?不用焦虑!

一个固定的工作流&#xff0c; 一个训练好的lora模型 输入一段提示词 二三十秒的时间&#xff0c;就能生成一张精致美观有韵味的中秋国风插画 这张不喜欢&#xff0c;改下提示词重新生成一张不一样的。还是二十几秒 同样的插画&#xff0c;你用手绘&#xff0c;从起稿到上…

大数据新视界 --大数据大厂之MongoDB与大数据:灵活文档数据库的应用场景

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

【设计模式-外观】

这里写自定义目录标题 定义UML图角色作用代码使用场景 定义 为子系统中一组相关接口提供一致界面&#xff0c;定义一个高级接口&#xff0c;使得子系统更加容易使用。 UML图 角色作用 外观&#xff08;Facade&#xff09;角色&#xff1a;这是外观模式的核心&#xff0c;它知…

MongoDB的详细安装教程

6、MongoDB安装 6.1 为什么使用MongoDB 性能好大规模数据存储&#xff08;可拓展性&#xff09;可靠安全&#xff08;本地复制、自动故障转移&#xff09;方便存储复杂数据结构 6.2 下载安装 【1】下载地址&#xff0c;这里下载的是5.0版本的&#xff0c;否则配置环境变量之…

【电路笔记】-差分运算放大器

差分运算放大器 文章目录 差分运算放大器1、概述2、差分运算放大器表示2.1 差分模式2.2 减法器模式3、差分放大器示例3.1 相关电阻3.2 惠斯通桥3.3 光/温度检测4、仪表放大器5、总结1、概述 在之前的文章中,我们讨论了反相运算放大器和同相运算放大器,我们考虑了在运算放大器…

revisiting拉普拉斯模板

二维向量的二阶微分是Hessian矩阵&#xff0c;拉普拉斯算子是将两个独立的二阶微分求和&#xff0c;对二阶微分的近似。 我不认同冈萨雷斯的8邻域拉普拉斯模板。 MATLAB图像处理工具箱中fspecial函数’laplacian’参数给的拉普拉斯模板&#xff1a; 对于数字滤波器&#xff…

中秋前夕-我居然使用技术来鞭策兄弟

中秋前夕-我居然使用技术来鞭策兄弟 前言 最近在带领一些小伙伴在完成功能&#xff0c;因为人数不少&#xff0c;那么我们如何统计大家有没有摸鱼偷懒呢&#xff1f; 聪明的朋友们可以想到&#xff0c;利用git的提交记录统计。 因为git提交时&#xff0c;会给我们带上一些关…

高德2.0 多边形覆盖物无法选中编辑

多边形覆盖物无法选中编辑。先检查一下数据的类型得是<number[]>,里面是字符串的虽然显示没问题&#xff0c;但是不能选中编辑。 &#xff08;在项目中排查了加载时机&#xff0c;事件监听…等等种种原因&#xff0c;就是没发现问题。突然想到可能是数据就有问题&#xf…