对应 LeetCode 752.转动转盘锁

### 问题定义

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

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

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

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

解决思路

  • 穷举

    这个问题比较简单,使用穷举的方式列出从 "0000" 开始满足所有条件的转动情况,进行转动分析即可

    这里的穷举使用 BFS 是一个很好的思路,每层的高度就对应着转动的次数,只要当前层中存在目标数字 \(target\) ,那么当前的层数就是其搜索的次数

  • 双向 BFS 优化

    由于每层的每个数字的都可以向上或向下转动,因此在搜索过程中将会出现 “搜索爆炸” 的情况。可选的解决方案交替使用从上和从下的搜索方式进行遍历,这样就能够有效地解决 “搜索爆炸” 的问题

细节处理:实际上,有些被搜索过的数字可能在之后会再次出现,因此需要记录之前已经搜索过的数字;使用双向搜索的方式进行搜索时,使用 Map 来记录两个方向的搜索次数,当搜索成功时相加即可。

实现

  • 一般的穷举

    class Solution {
    Set<String> deadSet = new HashSet<>();
    static String start = "0000";
    Set<String> accessed = new HashSet<>(); public int openLock(String[] deadends, String target) {
    for (String s : deadends) deadSet.add(s); // 特殊情况处理
    if (deadSet.contains(start) || deadSet.contains(target))
    return -1;
    if (target.equals(start)) return 0; Deque<String> deque = new LinkedList<>();
    deque.offer(start);
    accessed.add(start); int ans = 0;
    while (!deque.isEmpty()) {
    int size = deque.size();
    ans++; while (size-- > 0) {
    String word = deque.poll();
    for (int i = 0; i < 4; ++i) {
    String plus = plus(word.toCharArray(), i);
    if (!deadSet.contains(plus) && !accessed.contains(plus)) {
    if (plus.equals(target)) return ans;
    deque.offer(plus);
    accessed.add(plus);
    } String minus = minus(word.toCharArray(), i);
    if (!deadSet.contains(minus) && !accessed.contains(minus)) {
    if (minus.equals(target)) return ans;
    deque.offer(minus);
    accessed.add(minus);
    }
    }
    }
    } return -1;
    } // 指定的数字位 +1
    String plus(char[] array, int index) {
    if (array[index] < '9') array[index] = (char) (array[index] + 1);
    else array[index] = '0'; return String.valueOf(array);
    } // 指定的数字位 -1
    String minus(char[] array, int index) {
    if (array[index] > '0') array[index] = (char) (array[index] - 1);
    else array[index] = '9'; return String.valueOf(array);
    }
    }

    复杂度分析:略

  • 双向 BFS

    class Solution {
    Set<String> deadSet = new HashSet<>();
    static String start = "0000";
    Set<String> accessed = new HashSet<>(); public int openLock(String[] deadends, String target) {
    for (String s : deadends) deadSet.add(s); if (deadSet.contains(start) || deadSet.contains(target))
    return -1;
    if (target.equals(start)) return 0; Deque<String> top = new LinkedList<>();
    Deque<String> bottom = new LinkedList<>();
    Map<String, Integer> topMap = new HashMap<>();
    Map<String, Integer> bottomMap = new HashMap<>(); top.offer(start);
    topMap.put(start, 0); bottom.offer(target);
    bottomMap.put(target, 0); while (!top.isEmpty() && !bottom.isEmpty()) {
    int t = -1;
    if (top.size() <= bottom.size()) {
    t = update(top, bottom, topMap, bottomMap);
    } else {
    t = update(bottom, top, bottomMap, topMap);
    } if (t != -1) return t;
    } return -1;
    } int update(
    Deque<String> d1,
    Deque<String> d2,
    Map<String, Integer> map1,
    Map<String, Integer> map2
    ) {
    int size = d1.size(); while (size-- > 0) {
    String word = d1.poll(); for (int i = 0; i < 4; ++i) {
    String plus = plus(word.toCharArray(), i);
    if (!deadSet.contains(plus) && !map1.containsKey(plus)) {
    if (map2.containsKey(plus))
    return map1.get(word) + map2.get(plus) + 1; // 本次已经再转动了一次
    d1.offer(plus);
    map1.put(plus, map1.get(word) + 1);
    } String minus = minus(word.toCharArray(), i);
    if (!deadSet.contains(minus) && !map1.containsKey(minus)) {
    if (map2.containsKey(minus))
    return map1.get(word) + map2.get(minus) + 1;
    d1.offer(minus);
    map1.put(minus, map1.get(word) + 1);
    }
    }
    } return -1;
    } String plus(char[] array, int index) {
    if (array[index] < '9') array[index] = (char) (array[index] + 1);
    else array[index] = '0'; return String.valueOf(array);
    } String minus(char[] array, int index) {
    if (array[index] > '0') array[index] = (char) (array[index] - 1);
    else array[index] = '9'; return String.valueOf(array);
    }
    }

    复杂度分析:略

BFS(二)转动转盘锁的更多相关文章

  1. Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock)

    Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary ...

  2. [LeetCode] 0752. Open the Lock 打开转盘锁

    题目 You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', ' ...

  3. leetcode 752. 打开转盘锁

    地址 https://leetcode-cn.com/problems/open-the-lock/ 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', ...

  4. LeetCode 752:打开转盘锁 Open the Lock

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

  5. [Swift]LeetCode752. 打开转盘锁 | Open the Lock

    You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', ...

  6. JVM内部细节之二:偏向锁(Biased Locking)

    在前面一片文章<JVM内部细节之一:synchronized关键字及实现细节>中已经提到过偏向锁的概念,在理解什么是偏向锁前必须先理解什么是轻量级锁(Lightweight Locking ...

  7. Java实现 LeetCode 752 打开转盘锁(暴力)

    752. 打开转盘锁 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' .每个拨轮可以自由旋 ...

  8. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  9. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  10. python并发编程之多进程(二):互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型

    一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...

随机推荐

  1. mysql8安装踩坑记

    背景:已安装mysql5.7版本 问题一:默认的3306端口被占用 进入mysql5.7的my.ini文件,更改port为3307或者其他未被占用的端口 问题二:Install/Remove of t ...

  2. Vitess全局唯一ID生成的实现方案

    为了标识一段数据,通常我们会为其指定一个唯一id,比如利用MySQL数据库中的自增主键. 但是当数据量非常大时,仅靠数据库的自增主键是远远不够的,并且对于分布式数据库只依赖MySQL的自增id无法满足 ...

  3. dms

          产品解决方案文档与社区免费试用定价云市场合作伙伴支持与服务了解阿里云       备案控制台 首页关系型数据库NoSQL数据库数据仓库数据管理工具向量数据库免费试用 个人     打卡 发 ...

  4. QUERIES

    这个题解..u1s1我没看懂,但是我觉得这里面有一个重要的思想就是对于像异或这种最终值只是看一个数位的问题,我们可以考虑分解,把每一个子问题单独解决就可以了其实更难的应该是每个子区间的异或和之和这个方 ...

  5. SP3377

    题目简化和分析: 前言:这题目背景真奇怪. 我们可以将每种关系,看成一条边,如果出现奇数边环就不满足. 例如:\(a,b\) 异性 \(a,c\) 异性 \(b,c\)异性 这种情况是不满足的. 相当 ...

  6. CF276C

    题目简化和分析: 属于一种贪心思维,我们想如果要使得和最大,那么就必须保证最大的数乘的次数越多越好,并且排序没有限制,快速累加每个位置出现的次数,所以应该使用线段树差分. 然后排序最大乘最大累加. S ...

  7. 每天5分钟复习OpenStack(七)内存虚拟化

    标题中的存储虚拟化,涉及到两个方面,分别是内存和磁盘的虚拟化技术.内存的虚拟化就不得不提EPT和VPID 技术. 首先申明下本人其实不想写一些纯理论的东西,但是架不住面试经被问,为此特将一些特别复杂的 ...

  8. 【matplotlib 实战】--箱型图

    箱型图(Box Plot),也称为盒须图或盒式图,1977年由美国著名统计学家约翰·图基(John Tukey)发明.是一种用作显示一组数据分布情况的统计图,因型状如箱子而得名. 它能显示出一组数据的 ...

  9. Prometheus+Grafana实现服务性能监控:windows主机监控、Spring Boot监控、Spring Cloud Alibaba Seata监控

    1.Prometheus介绍 Prometheus使用Go语言开发,中文名称叫:普罗 米修斯.Prometheus是一个开源系统最初在SoundCloud构建的监控和警报工具包.自 2012 年成立以 ...

  10. 🔥🔥Java开发者的Python快速进修指南:函数基础

    话不多说,今天我们要介绍的是函数.本系列文章追求短而精,今天我们将重点讨论函数以及与Java方法的区别.与Java方法不同,函数不需要像Java方法一样讲究修饰符等其他特性,它只需要使用"d ...