BFS(二)转动转盘锁
### 问题定义
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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);
}
}
复杂度分析:略
双向
BFSclass 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(二)转动转盘锁的更多相关文章
- Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock)
Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary ...
- [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', ' ...
- leetcode 752. 打开转盘锁
地址 https://leetcode-cn.com/problems/open-the-lock/ 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', ...
- LeetCode 752:打开转盘锁 Open the Lock
题目: 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' .每个拨轮可以自由旋转:例如把 ' ...
- [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', ...
- JVM内部细节之二:偏向锁(Biased Locking)
在前面一片文章<JVM内部细节之一:synchronized关键字及实现细节>中已经提到过偏向锁的概念,在理解什么是偏向锁前必须先理解什么是轻量级锁(Lightweight Locking ...
- Java实现 LeetCode 752 打开转盘锁(暴力)
752. 打开转盘锁 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' .每个拨轮可以自由旋 ...
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...
- (删)Java线程同步实现二:Lock锁和Condition
在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...
- python并发编程之多进程(二):互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型
一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...
随机推荐
- 【vulnhub】——DC-9靶机
[vulnhub]--DC-9靶机 1. 主机发现 扫描kali主机C段(Kali和DC-9主机在同一个网关下): 发现主机为192.168.108.146,进行详细端口扫描: 可以看到靶机开了一个s ...
- [ARC143B] Counting Grids 题解
Counting Grids 题目大意 将 \(1\sim n^2\) 填入 \(n\times n\) 的网格 \(A\) 中,对于每个格子满足以下条件之一: 该列中存在大于它的数. 该行中存在小于 ...
- 🎊OpenTiny Vue 3.11.0 发布:增加富文本、ColorPicker等4个新组件,迎来了贡献者大爆发!
你好,我是 Kagol. 非常高兴跟大家宣布,2023年10月24日,OpenTiny Vue 发布了 v3.11.0 . OpenTiny 每次大版本发布,都会给大家带来一些实用的新特性,8.14 ...
- umich cv-6-2 注意力机制
这节课中介绍了循环神经网络的第二部分,主要引入了注意力机制,介绍了注意力机制的应用以及如何理解,在此基础上建立了注意力层以及transformer架构 注意力机制 注意力机制 应用与理解 注意力层 t ...
- 使用JNA读取dll文件
由于项目需要进行读卡操作,需要使用java进行读取dll文件 设备:德卡T10 1. 引入POM文件 <dependency> <groupId>net.java.dev.jn ...
- 自然语言处理历史史诗:NLP的范式演变与Python全实现
本文全面回顾了自然语言处理(NLP)从20世纪50年代至今的历史发展.从初创期的符号学派和随机学派,到理性主义时代的逻辑和规则范式,再到经验主义和深度学习时代的数据驱动方法,以及最近的大模型时代,NL ...
- Go 接口-契约介绍
Go 接口-契约介绍 目录 Go 接口-契约介绍 一.接口基本介绍 1.1 接口类型介绍 1.2 为什么要使用接口 1.3 面向接口编程 1.4 接口的定义 二.空接口 2.1 空接口的定义 2.2 ...
- TIOBE 11月榜单:Java和 C# 之间的差距缩小到0.7
TIOBE 公布了 2023 年 11 月的编程语言排行榜. 虽然这期重点介绍的是Kotlin,本月,它的排名上升了 0.17%,从第 18 位上升到第 15 位,前进了 3 位. TIOBE的10月 ...
- .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
2023年11月15日,对.net的开发圈是一个重大的日子,.net 8.0正式版发布. 圈内已经预热了有半个月有余,性能不断超越,开发体验越来越完美,早在.net 5.0的时候就各种吹风Aot编译, ...
- Excel 使用 VLOOKUP 函数匹配特定列
前言 工作有一项内容,是根据新的表格的某一列的内容一对一匹配,生成一列新的表格.这就用到了 Excel 的 VLOOKUP 函数. 函数使用 函数体: =VLOOKUP(lookup_value,ta ...