LC 752 Open the Lock

由于这个问题,涉及了很多知识,例如数据结构里面的哈希表,c++中的迭代器,因此,需要对于每一个疑惑逐一击破。

问题描述

You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. The wheels can rotate freely and wrap around: for example we can turn '9' to be '0', or '0' to be '9'. Each move consists of turning one wheel one slot.

The lock initially starts at '0000', a string representing the state of the 4 wheels.

You are given a list of deadends dead ends, meaning if the lock displays any of these codes, the wheels of the lock will stop turning and you will be unable to open it.

Given a target representing the value of the wheels that will unlock the lock, return the minimum total number of turns required to open the lock, or -1 if it is impossible.

Example 1:

Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6
Explanation:
A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202".
Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid,
because the wheels of the lock become stuck after the display becomes the dead end "0102".

Example 2:

Input: deadends = ["8888"], target = "0009"
Output: 1
Explanation:
We can turn the last wheel in reverse to move from "0000" -> "0009".

Example 3:

Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
Output: -1
Explanation:
We can't reach the target without getting stuck.

Example 4:

Input: deadends = ["0000"], target = "8888"
Output: -1

Note:

  1. The length of deadends will be in the range [1, 500].
  2. target will not be in the list deadends.
  3. Every string in deadends and the string target will be a string of 4 digits from the 10,000 possibilities '0000' to '9999'.

 

参考答案

 1 class Solution {
 2 public:
 3     int openLock(vector<string>& deadends, string target) {
 4         unordered_set<string> deadlock(deadends.begin(), deadends.end());
 5         if (deadlock.count("0000")) return -1;
 6         int res = 0;
 7         unordered_set<string> visited{{"0000"}};
 8         queue<string> q{{"0000"}};
 9         while (!q.empty()) {
10             ++res;
11             for (int k = q.size(); k > 0; --k) {
12                 auto t = q.front(); q.pop();
13                 for (int i = 0; i < t.size(); ++i) {
14                     for (int j = -1; j <= 1; ++j) {
15                         if (j == 0) continue;
16                         string str = t;
17                         str[i] = ((t[i] - '0') + 10 + j) % 10 + '0';
18                         if (str == target) return res;
19                         if (!visited.count(str) && !deadlock.count(str)) q.push(str);        
20                         visited.insert(str);
21                     }
22                 }
23             }
24         }
25         return -1;
26     }
27 };

 

补充知识

迭代器

正如我们知道的,程序中包含了大量的迭代,这些迭代都是在循环中进行的,比如for。

迭代(Iteration):是一种行为,对于某个特定容器,进行遍历的行为。

可迭代对象(Iterable):是一个容器,例如set,list,vector等等,一堆数据装在里面,而这个容器可以进行迭代操作。

那什么是迭代器(iterator)呢?

迭代器,就好像是一个可以供人们操作的装置对象,其中包含了数据,以及可以进行操作的按钮(例如c++中的begin,end等等[1]),这样我们可以很方便的使用并且操作打包好的数据。

另外还有一个问题,比如下面这个例子[2]:

std::vector<int> v;
for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
{
    // do someting with iter
}

v 能懂,是一个vector。vector<int> 类型的 iterator 也可以懂,但什么是 v.begin() ?

[1] 的解释是返回幵始迭代器。what the fxxk? 什么是开始迭代器?难道迭代器不是可以操纵的对象么?我查了很多的中文网站和博客,似乎都没有很理想的解释。后来查到了它的英文解释:begin() function is used to return an iterator pointing to the first element of the vector container.

 

这样就清楚好多了, v.begin() 是一个迭代器,特殊的是,它是指向容器中第一个元素的迭代器

另外,也要注意 v.end() ,它也是迭代器,但它是 指向容器最后元素的 下一个位置的 迭代器。

使用例子[3]

#include <iostream> 
#include <vector> 
using namespace std; 
  
int main() 
{ 
    // declaration of vector container 
    vector<int> myvector{ 1, 2, 3, 4, 5 }; 
  
    // using end() to print vector 
    for (auto it = myvector.begin() ; it != myvector.end(); ++it) 
        cout << ' ' << *it; 
    return 0; 
} 

 

输出为:

1 2 3 4 5

另外,不用费心考虑iteator的类型,直接使用auto即可

 

insert & find

使用迭代器的好处,就是可以使用一堆提供好的函数,这个例子中可以找到 insert 和 find 。
方式就如答案所示,详细的可以参看:http://c.biancheng.net/view/570.html
 

t[i] - '0' 是个什么?

第17行,出现了 str[i] = (( t[i] - '0' ) + 10 + j) % 10 + '0' 这一行,所以我陷入了迷思,为什么string类型可以进行运算。后来通过print进行测试。
发现,如果对'0'进行加减操作,会出现这样的结果。
运行如下代码:
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     string str = "0";
 9     string result ;
10     for(int i = -10; i<10;i++){
11         result[0] = str[0] + i;
12         cout<<result<<endl;
13     }
14 
15     return 0;
16 }

可以得到:

'
(
)
+
,
.
/
0
1
2
3
4
5
6
7
8
9

我们会发现,直接对 string 进行加减操作,直接会显示上一个字符和之后的字符。因此,17行源代码的意思是:

减去‘0’,即获得原来的 t[i] 对于‘0’的偏移量,这是一个 int 类型。基于该偏移量,进行加减1的操作(+j),然后再次添加‘0’,将偏移量变成 string 类型。而 +10 和 %10 操作,为了防止 0-1 = -1 操作的发生,或者是 9+1 = 10 的情况发生。-1对应着 9 (9 = 0+10-1),而 10 对应着 0 (0 = 9+1 +10 对 10 取余是 0)。

很舒服。

 

[1]: http://c.biancheng.net/view/409.html

[2]:https://qinglinmao8315.github.io/c++/2018/03/07/how-std-vector-end-work.html

[3]: https://www.geeksforgeeks.org/vectorbegin-vectorend-c-stl/

原文地址:https://www.cnblogs.com/kykai/p/11421708.html