|
89 | 89 |
|
90 | 90 | <!-- 这里可写通用的实现逻辑 -->
|
91 | 91 |
|
92 |
| -BFS 最小步数模型。 |
| 92 | +BFS 最小步数模型。本题搜索空间不大,可以直接使用朴素 BFS,以下题解中还提供了双向 BFS 的题解代码,仅供参考。 |
| 93 | + |
| 94 | +双向 BFS 是 BFS 常见的一个优化方法,主要实现思路如下: |
| 95 | + |
| 96 | +1. 创建两个队列 q1, q2 分别用于“起点 -> 终点”、“终点 -> 起点”两个方向的搜索; |
| 97 | +2. 创建两个哈希表 m1, m2 分别记录访问过的节点以及对应的扩展次数(步数); |
| 98 | +3. 每次搜索时,优先选择元素数量较少的队列进行搜索扩展,如果在扩展过程中,搜索到另一个方向已经访问过的节点,说明找到了最短路径; |
| 99 | +4. 只要其中一个队列为空,说明当前方向的搜索已经进行不下去了,说明起点到终点不连通,无需继续搜索。 |
| 100 | + |
| 101 | +```python |
| 102 | +while q1 and q2: |
| 103 | + if len(q1) <= len(q2): |
| 104 | + # 优先选择较少元素的队列进行扩展 |
| 105 | + extend(m1, m2, q1) |
| 106 | + else: |
| 107 | + extend(m2, m1, q2) |
| 108 | +def extend(m1, m2, q): |
| 109 | + # 新一轮扩展 |
| 110 | + for _ in range(len(q), 0, -1): |
| 111 | + p = q.popleft() |
| 112 | + step = m1[p] |
| 113 | + for t in next(p): |
| 114 | + if t in m1: |
| 115 | + # 此前已经访问过 |
| 116 | + continue |
| 117 | + if t in m2: |
| 118 | + # 另一个方向已经搜索过,说明找到了一条最短的连通路径 |
| 119 | + return step + 1 + m2[t] |
| 120 | + q.append(t) |
| 121 | + m1[t] = step + 1 |
| 122 | +``` |
93 | 123 |
|
94 | 124 | <!-- tabs:start -->
|
95 | 125 |
|
@@ -146,6 +176,42 @@ class Solution:
|
146 | 176 | return -1
|
147 | 177 | ```
|
148 | 178 |
|
| 179 | +双向 BFS: |
| 180 | + |
| 181 | +```python |
| 182 | +class Solution: |
| 183 | + def minimumOperations(self, nums: List[int], start: int, goal: int) -> int: |
| 184 | + def next(x): |
| 185 | + res = [] |
| 186 | + for num in nums: |
| 187 | + res.append(x + num) |
| 188 | + res.append(x - num) |
| 189 | + res.append(x ^ num) |
| 190 | + return res |
| 191 | + |
| 192 | + def extend(m1, m2, q): |
| 193 | + for _ in range(len(q), 0, -1): |
| 194 | + x = q.popleft() |
| 195 | + step = m1[x] |
| 196 | + for y in next(x): |
| 197 | + if y in m1: |
| 198 | + continue |
| 199 | + if y in m2: |
| 200 | + return step + 1 + m2[y] |
| 201 | + if 0 <= y <= 1000: |
| 202 | + m1[y] = step + 1 |
| 203 | + q.append(y) |
| 204 | + return -1 |
| 205 | + |
| 206 | + m1, m2 = {start: 0}, {goal: 0} |
| 207 | + q1, q2 = deque([start]), deque([goal]) |
| 208 | + while q1 and q2: |
| 209 | + t = extend(m1, m2, q1) if len(q1) <= len(q2) else extend(m2, m1, q2) |
| 210 | + if t != -1: |
| 211 | + return t |
| 212 | + return -1 |
| 213 | +``` |
| 214 | + |
149 | 215 | ### **Java**
|
150 | 216 |
|
151 | 217 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
@@ -218,6 +284,63 @@ class Solution {
|
218 | 284 | }
|
219 | 285 | ```
|
220 | 286 |
|
| 287 | +双向 BFS: |
| 288 | + |
| 289 | +```java |
| 290 | +class Solution { |
| 291 | + private int[] nums; |
| 292 | + |
| 293 | + public int minimumOperations(int[] nums, int start, int goal) { |
| 294 | + this.nums = nums; |
| 295 | + Map<Integer, Integer> m1 = new HashMap<>(); |
| 296 | + Map<Integer, Integer> m2 = new HashMap<>(); |
| 297 | + Deque<Integer> q1 = new ArrayDeque<>(); |
| 298 | + Deque<Integer> q2 = new ArrayDeque<>(); |
| 299 | + m1.put(start, 0); |
| 300 | + m2.put(goal, 0); |
| 301 | + q1.offer(start); |
| 302 | + q2.offer(goal); |
| 303 | + while (!q1.isEmpty() && !q2.isEmpty()) { |
| 304 | + int t = q1.size() <= q2.size() ? extend(m1, m2, q1) : extend(m2, m1, q2); |
| 305 | + if (t != -1) { |
| 306 | + return t; |
| 307 | + } |
| 308 | + } |
| 309 | + return -1; |
| 310 | + } |
| 311 | + |
| 312 | + private int extend(Map<Integer, Integer> m1, Map<Integer, Integer> m2, Deque<Integer> q) { |
| 313 | + for (int i = q.size(); i > 0; --i) { |
| 314 | + int x = q.poll(); |
| 315 | + int step = m1.get(x); |
| 316 | + for (int y : next(x)) { |
| 317 | + if (m1.containsKey(y)) { |
| 318 | + continue; |
| 319 | + } |
| 320 | + if (m2.containsKey(y)) { |
| 321 | + return step + 1 + m2.get(y); |
| 322 | + } |
| 323 | + if (y >= 0 && y <= 1000) { |
| 324 | + m1.put(y, step + 1); |
| 325 | + q.offer(y); |
| 326 | + } |
| 327 | + } |
| 328 | + } |
| 329 | + return -1; |
| 330 | + } |
| 331 | + |
| 332 | + private List<Integer> next(int x) { |
| 333 | + List<Integer> res = new ArrayList<>(); |
| 334 | + for (int num : nums) { |
| 335 | + res.add(x + num); |
| 336 | + res.add(x - num); |
| 337 | + res.add(x ^ num); |
| 338 | + } |
| 339 | + return res; |
| 340 | + } |
| 341 | +} |
| 342 | +``` |
| 343 | + |
221 | 344 | ### **C++**
|
222 | 345 |
|
223 | 346 | ```cpp
|
@@ -295,6 +418,59 @@ public:
|
295 | 418 | };
|
296 | 419 | ```
|
297 | 420 |
|
| 421 | +双向 BFS: |
| 422 | + |
| 423 | +```cpp |
| 424 | +class Solution { |
| 425 | +public: |
| 426 | + int minimumOperations(vector<int>& nums, int start, int goal) { |
| 427 | + unordered_map<int, int> m1; |
| 428 | + unordered_map<int, int> m2; |
| 429 | + m1[start] = 0; |
| 430 | + m2[goal] = 0; |
| 431 | + queue<int> q1{{start}}; |
| 432 | + queue<int> q2{{goal}}; |
| 433 | + while (!q1.empty() && !q2.empty()) |
| 434 | + { |
| 435 | + int t = q1.size() <= q2.size() ? extend(m1, m2, q1, nums) : extend(m2, m1, q2, nums); |
| 436 | + if (t != -1) return t; |
| 437 | + } |
| 438 | + return -1; |
| 439 | + } |
| 440 | + |
| 441 | + int extend(unordered_map<int, int>& m1, unordered_map<int, int>& m2, queue<int>& q, vector<int>& nums) { |
| 442 | + for (int i = q.size(); i > 0; --i) |
| 443 | + { |
| 444 | + int x = q.front(); |
| 445 | + int step = m1[x]; |
| 446 | + q.pop(); |
| 447 | + for (int y : next(nums, x)) |
| 448 | + { |
| 449 | + if (m1.count(y)) continue; |
| 450 | + if (m2.count(y)) return step + 1 + m2[y]; |
| 451 | + if (y >= 0 && y <= 1000) |
| 452 | + { |
| 453 | + m1[y] = step + 1; |
| 454 | + q.push(y); |
| 455 | + } |
| 456 | + } |
| 457 | + } |
| 458 | + return -1; |
| 459 | + } |
| 460 | + |
| 461 | + vector<int> next(vector<int>& nums, int x) { |
| 462 | + vector<int> res; |
| 463 | + for (int num : nums) |
| 464 | + { |
| 465 | + res.push_back(x + num); |
| 466 | + res.push_back(x - num); |
| 467 | + res.push_back(x ^ num); |
| 468 | + } |
| 469 | + return res; |
| 470 | + } |
| 471 | +}; |
| 472 | +``` |
| 473 | +
|
298 | 474 | ### **Go**
|
299 | 475 |
|
300 | 476 | ```go
|
@@ -364,6 +540,53 @@ func minimumOperations(nums []int, start int, goal int) int {
|
364 | 540 | }
|
365 | 541 | ```
|
366 | 542 |
|
| 543 | +双向 BFS: |
| 544 | + |
| 545 | +```go |
| 546 | +func minimumOperations(nums []int, start int, goal int) int { |
| 547 | + next := func(x int) []int { |
| 548 | + var res []int |
| 549 | + for _, num := range nums { |
| 550 | + res = append(res, []int{x + num, x - num, x ^ num}...) |
| 551 | + } |
| 552 | + return res |
| 553 | + } |
| 554 | + m1, m2 := map[int]int{start: 0}, map[int]int{goal: 0} |
| 555 | + q1, q2 := []int{start}, []int{goal} |
| 556 | + extend := func() int { |
| 557 | + for i := len(q1); i > 0; i-- { |
| 558 | + x := q1[0] |
| 559 | + q1 = q1[1:] |
| 560 | + step, _ := m1[x] |
| 561 | + for _, y := range next(x) { |
| 562 | + if _, ok := m1[y]; ok { |
| 563 | + continue |
| 564 | + } |
| 565 | + if v, ok := m2[y]; ok { |
| 566 | + return step + 1 + v |
| 567 | + } |
| 568 | + if y >= 0 && y <= 1000 { |
| 569 | + m1[y] = step + 1 |
| 570 | + q1 = append(q1, y) |
| 571 | + } |
| 572 | + } |
| 573 | + } |
| 574 | + return -1 |
| 575 | + } |
| 576 | + for len(q1) > 0 && len(q2) > 0 { |
| 577 | + if len(q1) > len(q2) { |
| 578 | + m1, m2 = m2, m1 |
| 579 | + q1, q2 = q2, q1 |
| 580 | + } |
| 581 | + t := extend() |
| 582 | + if t != -1 { |
| 583 | + return t |
| 584 | + } |
| 585 | + } |
| 586 | + return -1 |
| 587 | +} |
| 588 | +``` |
| 589 | + |
367 | 590 | ### **TypeScript**
|
368 | 591 |
|
369 | 592 | ```ts
|
|
0 commit comments