leetcode_打开转盘锁

一、题目解析

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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
解释:
无法旋转到目标数字且不被锁定。

示例 4:

输入: deadends = ["0000"], target = "8888"
输出:-1

提示:

死亡列表 deadends 的长度范围为 [1, 500]。 目标数字 target 不会在 deadends 之中。 每个
deadends 和 target 中的字符串的数字会在 10,000 个可能的情况 ‘0000’ 到 ‘9999’ 中产生。

二、思路详解

其实这道题和迷宫题很像。迷宫题是通路可走,墙壁不能走。而这个开锁的题目是遇到 deadends 里的数字不能拨动,要绕开死亡数字去找到最终的结题答案。然后这种题目我们可以优先想到广度优先算法。其实这道题的结题思路还是比较模板式的。

2.1 题目分析
  • 密码锁为 0000 − 9999 可 以 1 0 4 = 10000 种 组 合 即 10000 个 节 点 0000-9999可以10^4=10000种组合即10000个节点 00009999104=1000010000
  • 时 间 复 杂 度 : O ( 8 × 10000 ) 时间复杂度:O(8\times 10000) O(8×10000) 四位密码锁每一位可以往上或往下拨动,及每个节点可以往8个方向扩展。
  • 空 间 复 杂 度 : O ( 10000 + d e a d e n d s ) 实 际 上 d e a d e n d s 的 长 度 范 围 为 [ 1 , 500 ] , 长 度 可 忽 略 。 空间复杂度:O(10000+deadends) 实际上deadends的长度范围为[1,500],长度可忽略。 O(10000+deadends)deadends[1,500]

遍历过程图解:
在这里插入图片描述

2.2 题目技巧点
  • 将vector的死亡列表存为Hash_set这样更方便查找。

    ​unordered_set<string> deadends_set(deadends.begin(), deadends.end());
    
  • 字符转整型

    ‘9’-‘0’=int类型的9 可以参考ASCII码即可弄明白
    
  • 控制拨动范围0-9

    常规取余操作
    

三、代码实现

#include <iostream>
#include <vector>
#include <unordered_set>
#include <queue>
using namespace std;

class Solution {
public:
	int openLock(vector<string>& deadends, string target) {
		const string start = "0000";
		//转成hash_set的原因:如果是用vector去查找当前目标是否在deadends中会慢
		//而hash_set就会比较快且方便。
		unordered_set<string> deadends_set(deadends.begin(), deadends.end());
		//两种特殊情况,如果起始状态直接就在deadends里头和起始状态就是目标值
		if (deadends_set.count(start)) return -1; //直接无解返回-1
		if (start == target) return 0; //直接解决,返回0

		queue<string> que;
		//访问过的节点存放到visited当中,hash_set也是为了好访问
		unordered_set<string> visited{ start };

		int steps = 0;
		que.push(start);
		while (!que.empty()){
			steps++;
			//size标记当前循环有多少个节点。后面循环的时候之去扩展这么多节点
			//因为在循环过程中que会更新,size会增加
			const int size = que.size(); 
			for (int i = 0; i < size; i++) {
				const string curr = que.front();
				que.pop();
				for (int j = 0; j < 4; j++) { //四位密码锁
					for (int k = -1; k <= 1; k += 2) { //上下拨 -1和1
						string next = curr;
						//技巧点:减去字符串0就会变成整型然后加上拨动方向
						//+10为了防止出现负数
						next[j] = (next[j] - '0' + k + 10) % 10 + '0';
						if (next == target) 
							return steps;
						if (deadends_set.count(next) || visited.count(next)) 
							continue;
						que.push(next);
						visited.insert(next);
					}
				}
			}
		}	
		return -1;
	}
};

四、参考链接

https://leetcode-cn.com/explore/learn/card/queue-stack/217/queue-and-bfs/873/

http://zxi.mytechroad.com/blog/searching/leetcode-752-open-the-lock/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值