|
| 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 | + |
| 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 | +classSolution: |
| 94 | +defopenLock(self,deadends: List[str],target_digits:str) ->int: |
| 95 | +if'0000'== target_digits: |
| 96 | +return0 |
| 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 | +whileself.queue: |
| 108 | + result+=1 |
| 109 | + queue_size=len(self.queue) |
| 110 | + |
| 111 | +for _inrange(queue_size): |
| 112 | + digits=self.queue.popleft() |
| 113 | + |
| 114 | +ifself.turn_one_slot(digits): |
| 115 | +return result |
| 116 | + |
| 117 | +return-1 |
| 118 | + |
| 119 | +defturn_one_slot(self,digits): |
| 120 | +for iinrange(len(digits)): |
| 121 | +for digitin closest_digits(int(digits[i])): |
| 122 | + new_digits=f'{digits[:i]}{digit}{digits[i+1:]}' |
| 123 | + |
| 124 | +if new_digits==self.target_digits: |
| 125 | +returnTrue |
| 126 | + |
| 127 | +if new_digitsinself.deadends: |
| 128 | +continue |
| 129 | + |
| 130 | +self.queue.append(new_digits) |
| 131 | +self.deadends.add(new_digits) |
| 132 | + |
| 133 | + |
| 134 | +defclosest_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 | +classSolution: |
| 147 | +defopenLock(self,deadends: List[str],target_digits:str) ->int: |
| 148 | +if'0000'== target_digits: |
| 149 | +return0 |
| 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 iinrange(4): |
| 163 | +for digitin 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_digitsin 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 | +defdistance(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 iinrange(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 | +defclosest_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 | +``` |