Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c2a74a9

Browse files
committed
752-open-the-lock.md Added Python solution in 2 ways.
1 parent b43e110 commit c2a74a9

File tree

2 files changed

+475
-0
lines changed

2 files changed

+475
-0
lines changed

en/1-1000/752-open-the-lock.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# 752. Open the Lock - Best Practices of LeetCode Solutions
2+
LeetCode link: [752. Open the Lock](https://leetcode.com/problems/open-the-lock), difficulty: **Medium**.
3+
4+
## LeetCode description of "752. Open the Lock"
5+
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.
6+
7+
The lock initially starts at `0000`, a string representing the state of the 4 wheels.
8+
9+
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.
10+
11+
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_.
12+
13+
### [Example 1]
14+
**Input**: `deadends = ["0201","0101","0102","1212","2002"], target = "0202"`
15+
16+
**Output**: `6`
17+
18+
**Explanation**:
19+
```
20+
A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202".
21+
Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid,
22+
because the wheels of the lock become stuck after the display becomes the dead end "0102".
23+
```
24+
25+
### [Example 2]
26+
**Input**: `deadends = ["8888"], target = "0009"`
27+
28+
**Output**: `1`
29+
30+
**Explanation**:
31+
```
32+
We can turn the last wheel in reverse to move from "0000" -> "0009".
33+
```
34+
35+
### [Example 3]
36+
**Input**: `["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"`
37+
38+
**Output**: `-1`
39+
40+
**Explanation**: `We cannot reach the target without getting stuck.`
41+
42+
### [Constraints]
43+
- `1 <= deadends.length <= 500`
44+
- `deadends[i].length == 4`
45+
- `target.length == 4`
46+
- `target` **will not be** in the list `deadends`.
47+
- `target` and `deadends[i]` consist of digits only.
48+
49+
### [Hints]
50+
<details>
51+
<summary>Hint 1</summary>
52+
We can think of this problem as a shortest path problem on a graph: there are `10000` nodes (strings `'0000'` to `'9999'`), and there is an edge between two nodes if they differ in one digit, that digit differs by 1 (wrapping around, so `'0'` and `'9'` differ by 1), and if *both* nodes are not in `deadends`.
53+
</details>
54+
55+
## Intuition
56+
We can think of this problem as a shortest path problem on a graph: there are `10000` nodes (strings `'0000'` to `'9999'`), and there is an edge between two nodes if they differ in one digit, that digit differs by 1 (wrapping around, so `'0'` and `'9'` differ by 1), and if *both* nodes are not in `deadends`.
57+
58+
### Solution 1: Breadth-First Search
59+
![](../../images/binary_tree_BFS_1.gif)
60+
61+
* As shown in the figure above, **Breadth-First Search** can be thought of as visiting vertices in rounds and rounds. Actually, whenever you see a question is about
62+
getting `minimum number` of something of a graph, `Breadth-First Search` would probably help.
63+
64+
* `Breadth-First Search` emphasizes first-in-first-out, so a **queue** is needed.
65+
66+
### Solution 2: A* (A-Star) Algorithm
67+
68+
**A-Star Algorithm** can be used to improve the performance of **Breadth-First Search Algorithm**.
69+
70+
**Breadth-First Search** treats each vertex equally, which inevitably leads to poor performance.
71+
72+
**A-Star Algorithm** calculates the **distance** between each `vertex` and the `target vertex`, and prioritizes vertices with close distances, which greatly improves performance.
73+
74+
#### A* (A-Star) Algorithm Has Two (or Three) Key Actions
75+
1. Use `priority_queue`.
76+
2. The function for calculating `distance` should be **carefully designed** (poor design will lead to **subtle** errors in the results).
77+
3. In special cases, simply using `distance` as the sorting basis for `priority_queue` is not enough, and you need to **make some adjustments**, such as adding it to the `step_count` variable value (not involved in this example).
78+
79+
## Complexity
80+
> `N` is the length of `deadends`.
81+
82+
### Solution 1: Breadth-First Search
83+
* Time: `O(10^4)`.
84+
* Space: `O(N)`.
85+
86+
### Solution 2: A* (A-Star) Algorithm
87+
* Time: `O(5 * 4 * 4 * 2 + N)`.
88+
* Space: `O(N)`.
89+
90+
## Python
91+
### Solution 1: Breadth-First Search
92+
```python
93+
class Solution:
94+
def openLock(self, deadends: List[str], target_digits: str) -> int:
95+
if '0000' == target_digits:
96+
return 0
97+
98+
self.deadends = set(deadends)
99+
if '0000' in deadends:
100+
return -1
101+
102+
self.queue = deque(['0000'])
103+
self.deadends.add('0000')
104+
self.target_digits = target_digits
105+
result = 0
106+
107+
while self.queue:
108+
result += 1
109+
queue_size = len(self.queue)
110+
111+
for _ in range(queue_size):
112+
digits = self.queue.popleft()
113+
114+
if self.turn_one_slot(digits):
115+
return result
116+
117+
return -1
118+
119+
def turn_one_slot(self, digits):
120+
for i in range(len(digits)):
121+
for digit in closest_digits(int(digits[i])):
122+
new_digits = f'{digits[:i]}{digit}{digits[i + 1:]}'
123+
124+
if new_digits == self.target_digits:
125+
return True
126+
127+
if new_digits in self.deadends:
128+
continue
129+
130+
self.queue.append(new_digits)
131+
self.deadends.add(new_digits)
132+
133+
134+
def closest_digits(digit):
135+
if digit == 0:
136+
return (9, 1)
137+
138+
if digit == 9:
139+
return (8, 0)
140+
141+
return (digit - 1, digit + 1)
142+
```
143+
144+
### Solution 2: A* (A-Star) Algorithm
145+
```python
146+
class Solution:
147+
def openLock(self, deadends: List[str], target_digits: str) -> int:
148+
if '0000' == target_digits:
149+
return 0
150+
151+
deadends = set(deadends)
152+
if '0000' in deadends:
153+
return -1
154+
155+
self.target_digits = target_digits
156+
157+
priority_queue = [(self.distance('0000'), '0000', 0)]
158+
159+
while priority_queue:
160+
_, digits, turns = heapq.heappop(priority_queue)
161+
162+
for i in range(4):
163+
for digit in closest_digits(int(digits[i])):
164+
new_digits = f'{digits[:i]}{digit}{digits[i + 1:]}'
165+
166+
if new_digits == target_digits:
167+
return turns + 1
168+
169+
if new_digits in deadends:
170+
continue
171+
172+
heapq.heappush(
173+
priority_queue,
174+
(self.distance(new_digits), new_digits, turns + 1)
175+
)
176+
deadends.add(new_digits)
177+
178+
return -1
179+
180+
def distance(self, digits):
181+
result = 0
182+
183+
# 0 1 2 3 4 5 6 7 8 9
184+
# 0 1 2 3 4 5 6 7 8 9
185+
# 0 1 2 3 4 5 6 7 8 9
186+
# 0 1 2 3 4 5 6 7 8 9
187+
for i in range(4):
188+
# 'Euclidean Distance' is widely used in 'A* Algorithm'.
189+
result += abs(int(self.target_digits[i]) - int(digits[i])) ** 2
190+
191+
return result ** 0.5
192+
193+
194+
def closest_digits(digit):
195+
if digit == 0:
196+
return (9, 1)
197+
198+
if digit == 9:
199+
return (8, 0)
200+
201+
return (digit - 1, digit + 1)
202+
```
203+
204+
## JavaScript
205+
```javascript
206+
// Welcome to create a PR to complete the code of this language, thanks!
207+
```
208+
209+
## Java
210+
```java
211+
// Welcome to create a PR to complete the code of this language, thanks!
212+
```
213+
214+
## C++
215+
```cpp
216+
// Welcome to create a PR to complete the code of this language, thanks!
217+
```
218+
219+
## C#
220+
```c#
221+
// Welcome to create a PR to complete the code of this language, thanks!
222+
```
223+
224+
## Go
225+
```go
226+
// Welcome to create a PR to complete the code of this language, thanks!
227+
```
228+
229+
## Ruby
230+
```ruby
231+
# Welcome to create a PR to complete the code of this language, thanks!
232+
```
233+
234+
## C, Kotlin, Swift, Rust or other languages
235+
```
236+
// Welcome to create a PR to complete the code of this language, thanks!
237+
```

0 commit comments

Comments
 (0)