Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 76d0175

Browse files
✨feat: Add 1345
1 parent 10ee25d commit 76d0175

File tree

2 files changed

+350
-0
lines changed

2 files changed

+350
-0
lines changed

‎Index/图论 BFS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| [1034. 边界着色](https://leetcode-cn.com/problems/coloring-a-border/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/coloring-a-border/solution/gong-shui-san-xie-tu-lun-sou-suo-zhuan-t-snvw/) | 中等 | 🤩🤩🤩🤩 |
1212
| [1036. 逃离大迷宫](https://leetcode-cn.com/problems/escape-a-large-maze/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/escape-a-large-maze/solution/gong-shui-san-xie-bfs-gei-ding-zhang-ai-8w63o/) | 中等 | 🤩🤩🤩🤩 |
1313
| [1162. 地图分析](https://leetcode-cn.com/problems/as-far-from-land-as-possible/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/as-far-from-land-as-possible/solution/gong-shui-san-xie-ru-he-shi-yong-duo-yua-vlea/) | 中等 | 🤩🤩🤩🤩 |
14+
| [1345. 跳跃游戏 IV](https://leetcode-cn.com/problems/jump-game-iv/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/jump-game-iv/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-q9tb1/) | 困难 | 🤩🤩🤩🤩🤩 |
1415
| [2059. 转化数字的最小运算数](https://leetcode-cn.com/problems/minimum-operations-to-convert-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-operations-to-convert-number/solution/gong-shui-san-xie-shuang-xiang-bfs-mo-ba-uckg/) | 中等 | 🤩🤩🤩🤩🤩 |
1516
| [LCP 07. 传递信息](https://leetcode-cn.com/problems/chuan-di-xin-xi/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/chuan-di-xin-xi/solution/gong-shui-san-xie-tu-lun-sou-suo-yu-dong-cyxo/) | 简单 | 🤩🤩🤩🤩 |
1617

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[1345. 跳跃游戏 IV](https://leetcode-cn.com/problems/jump-game-iv/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-q9tb1/)** ,难度为 **困难**
4+
5+
Tag : 「图论 BFS」、「双向 BFS」
6+
7+
8+
9+
给你一个整数数组 `arr` ,你一开始在数组的第一个元素处(下标为 `0`)。
10+
11+
每一步,你可以从下标 i 跳到下标:
12+
* `i + 1` 满足:`i + 1 < arr.length`
13+
* `i - 1` 满足:`i - 1 >= 0`
14+
* `j` 满足:`arr[i] == arr[j]``i != j`
15+
16+
请你返回到达数组最后一个元素的下标处所需的 **最少操作次数**
17+
18+
注意:任何时候你都不能跳到数组外面。
19+
20+
示例 1:
21+
```
22+
输入:arr = [100,-23,-23,404,100,23,23,23,3,404]
23+
24+
输出:3
25+
26+
解释:那你需要跳跃 3 次,下标依次为 0 --> 4 --> 3 --> 9 。下标 9 为数组的最后一个元素的下标。
27+
```
28+
示例 2:
29+
```
30+
输入:arr = [7]
31+
32+
输出:0
33+
34+
解释:一开始就在最后一个元素处,所以你不需要跳跃。
35+
```
36+
示例 3:
37+
```
38+
输入:arr = [7,6,9,6,9,6,9,7]
39+
40+
输出:1
41+
42+
解释:你可以直接从下标 0 处跳到下标 7 处,也就是数组的最后一个元素处。
43+
```
44+
示例 4:
45+
```
46+
输入:arr = [6,1,9]
47+
48+
输出:2
49+
```
50+
示例 5:
51+
```
52+
输入:arr = [11,22,7,7,7,7,7,7,7,22,13]
53+
54+
输出:3
55+
```
56+
57+
提示:
58+
* 1ドル <= arr.length <= 5 * 10^4$
59+
* $-10^8 <= arr[i] <= 10^8$
60+
61+
---
62+
63+
### 单向 BFS
64+
65+
根据跳跃规则,我们能够进行「前后跳」和「等值跳」,问题为到达结尾位置的最少步数,容易想到 `BFS`
66+
67+
为了方便进行「等值跳」,我们可以先使用「哈希表」记录某个值有哪些下标。
68+
69+
在进行 `BFS` 时,假如当前走到的位置为 $t,ドル我们尝试将 $t - 1$、$t + 1$ 和与 $arr[t]$ 等值的位置进行入队,为了防止重复同队,我们可以使用 $dist$ 数组记录到达某个位置的最小步数(初始化为 `INF`),只有 $dist[ne]$ 为 `INF` 时,该点没有被遍历过,可以入队并更新最小步数。
70+
71+
但光使用 $dist$ 还不能确保复杂度为 $O(n),ドル因为每次都需要遍历与 $arr[t]$ 等值的下标,为确保等值下标的遍历只会发生一次,我们需要在将等值下标添加到队列后,将 $arr[t]$ 从哈希表中移除。
72+
73+
容易证明每次将于 $arr[t]$ 的等值元素添加到队列后,将 $arr[t]$ 从哈希表中移除的正确性:
74+
75+
首次检索到 $arr[t]$ 值时,必然是最小步数,记为 $step,ドル此时 `BFS` 做法将其他等值下标距离更新为 $step + 1$:
76+
77+
* 若 $arr[t]$ 与结尾元素值相等,且 $t$ 为 $n - 1,ドル此时 $step$ 即是答案;
78+
* 若 $arr[t]$ 与结尾元素值相等,但 $t$ 不为 $n - 1,ドル此时会再跳一步到达结尾位置,即 $step + 1$ 为答案。那么是否可能存在使用比 $step + 1$ 更小的步数,也能到达结尾的位置呢?
79+
答案是:**可能存在,但如果最后是通过「等值跳」到达结尾位置的话,不可能存在比 $step + 1$ 更小的步数。**
80+
由于我们每次加入等值时都会进行哈希表的移除,因此到达 $t$ 的方式不可能是「等值跳」,而只能是「前后跳」。
81+
82+
* 假设是通过前跳到达位置 $t,ドル即点分布如图,步数满足等于 $step + 1$:
83+
84+
![image.png](https://pic.leetcode-cn.com/1642719041-FilgSH-image.png)
85+
86+
* 假设是通过后跳到达位置 $t,ドル即点分布如图,步数满足「如果是等值跳到达结尾,步数为 $step + 1$」:
87+
88+
![image.png](https://pic.leetcode-cn.com/1642719484-DdDvQl-image.png)
89+
90+
**综上,如果 $n - 1$ 是经过「等值跳」加入队列的话,起所能达到的最小步数必然为发起点 $t$ 的最小步数 $+1$。**
91+
92+
**也就是说,即使首次等值跳,加入队列后会将其从哈希表中进行移除,正确性也是可以保证的。**
93+
94+
基于此,我们可以额外增加一个 trick,就是在构建哈希表的时候,使用「倒序」的形式构建等值下标列表,这样可以确保如果最后位置是通过「等值跳」而来是,能够优先出队。
95+
96+
**代码(感谢 [@Benhao](/u/himymben/)[@🍭可乐可乐吗](/u/littletime_cc/) 同学提供的其他语言版本):**
97+
```Java
98+
class Solution {
99+
int INF = 0x3f3f3f3f;
100+
public int minJumps(int[] arr) {
101+
int n = arr.length;
102+
Map<Integer, List<Integer>> map = new HashMap<>();
103+
// 倒序插入 list,相当于给 deque 增加一个同层「下标越大,优先出队」的作用
104+
for (int i = n - 1; i >= 0; i--) {
105+
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
106+
list.add(i);
107+
map.put(arr[i], list);
108+
}
109+
int[] dist = new int[n];
110+
Arrays.fill(dist, INF);
111+
Deque<Integer> d = new ArrayDeque<>();
112+
d.addLast(0);
113+
dist[0] = 0;
114+
while (!d.isEmpty()) {
115+
int t = d.pollFirst(), step = dist[t];
116+
if (t == n - 1) return step;
117+
if (t + 1 < n && dist[t + 1] == INF) {
118+
d.addLast(t + 1);
119+
dist[t + 1] = step + 1;
120+
}
121+
if (t - 1 >= 0 && dist[t - 1] == INF) {
122+
d.addLast(t - 1);
123+
dist[t - 1] = step + 1;
124+
}
125+
List<Integer> list = map.getOrDefault(arr[t], new ArrayList<>());
126+
for (int ne : list) {
127+
if (dist[ne] == INF) {
128+
d.addLast(ne);
129+
dist[ne] = step + 1;
130+
}
131+
}
132+
map.remove(arr[t]);
133+
}
134+
return -1; // never
135+
}
136+
}
137+
```
138+
-
139+
```C++
140+
class Solution {
141+
public:
142+
int minJumps(vector<int>& arr) {
143+
const int inf = 0x3f3f3f3f;
144+
int n = arr.size();
145+
unordered_map<int, vector<int>> map;
146+
for(int i = n - 1; ~i; i--) {
147+
map[arr[i]].push_back(i);
148+
}
149+
vector<int> dist(n, inf);
150+
queue<int> q;
151+
q.push(0);
152+
dist[0] = 0;
153+
while(q.size()) {
154+
auto t = q.front(), step = dist[t];
155+
q.pop();
156+
if(t == n - 1) return step;
157+
if(t + 1 < n and dist[t + 1] == inf) {
158+
q.push(t + 1);
159+
dist[t + 1] = step + 1;
160+
}
161+
if(t - 1 >= 0 and dist[t - 1] == inf) {
162+
q.push(t - 1);
163+
dist[t - 1] = step + 1;
164+
}
165+
const auto& list = map[arr[t]];
166+
for(auto ne :list) {
167+
if(dist[ne] == inf) {
168+
q.push(ne);
169+
dist[ne] = step + 1;
170+
}
171+
}
172+
map[arr[t]].clear(); //or map.erase(arr[t]);
173+
}
174+
return -1;
175+
}
176+
};
177+
```
178+
-
179+
```python
180+
class Solution:
181+
def minJumps(self, arr: List[int]) -> int:
182+
n = len(arr)
183+
mp = defaultdict(list)
184+
for i, num in enumerate(arr):
185+
mp[num].append(i)
186+
dist = [inf] * n
187+
d = deque([0])
188+
dist[0] = 0
189+
while len(d) > 0:
190+
t = d.popleft()
191+
step = dist[t]
192+
if t == n - 1:
193+
return step
194+
for ne in mp[arr[t]]:
195+
if dist[ne] == inf:
196+
d.append(ne)
197+
dist[ne] = step + 1
198+
mp.pop(arr[t])
199+
if dist[t + 1] == inf:
200+
d.append(t + 1)
201+
dist[t + 1] = step + 1
202+
if t and dist[t - 1] == inf:
203+
d.append(t - 1)
204+
dist[t - 1] = step + 1
205+
return -1
206+
```
207+
-
208+
```Go
209+
const INF int = 0x3f3f3f3f
210+
func minJumps(arr []int) int {
211+
n := len(arr)
212+
mp := map[int][]int{}
213+
dist := make([]int, len(arr))
214+
for i := 0; i < n; i++{
215+
list := mp[arr[i]]
216+
list = append(list, i)
217+
mp[arr[i]] = list
218+
dist[i] = INF
219+
}
220+
d := []int{0}
221+
dist[0] = 0
222+
for len(d) > 0{
223+
t := d[0]
224+
step := dist[t]
225+
if t == n - 1{
226+
return step
227+
}
228+
d = d[1:]
229+
list := mp[arr[t]]
230+
delete(mp, arr[t])
231+
list = append(list, t + 1)
232+
if t > 0 {
233+
list = append(list, t - 1)
234+
}
235+
for _, ne := range list {
236+
if dist[ne] == INF {
237+
dist[ne] = step + 1
238+
d = append(d, ne)
239+
}
240+
}
241+
}
242+
return -1
243+
}
244+
```
245+
* 时间复杂度:预处理出 `map` 的复杂度为 $O(n)$;跑一遍 `BFS` 得到答案复杂度为 $O(n)$。整体复杂度为 $O(n)$
246+
* 空间复杂度:$O(n)$
247+
248+
---
249+
250+
### 双向 BFS
251+
252+
自然也能够使用「双向 `BFS`」进行求解。
253+
254+
不了解「双向 `BFS`」的同学,可以先看前置🧀:[【图论搜索专题】如何使用「双向 BFS」解决搜索空间爆炸问题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247486981&idx=1&sn=045ea6c880080fea1ce807794ccff69b&chksm=fd9ca51acaeb2c0c83d13e3b2a5196895d1a1b44f8981cc3efad9d6a2af158267010646cc262&token=1446568490&lang=zh_CN#rd) & [【图论搜索专题】双向 BFS 模板题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489502&idx=1&sn=dc863d4bc71c4739a4799b9a4558bd01&chksm=fd9cbcc1caeb35d749d0d72f485485527482c27b608c8f4062c29a997ede97a09ce598b58c7f&token=1446568490&lang=zh_CN#rd)
255+
256+
双向 `BFS` 能够有效解决搜索空间爆炸问题,本题使用双向 `BFS` 的话,可以不进行哈希表的 `remove` 操作。
257+
258+
代码:
259+
```Java
260+
class Solution {
261+
int[] arr;
262+
int INF = 0x3f3f3f3f;
263+
int n;
264+
Map<Integer, List<Integer>> map = new HashMap<>();
265+
public int minJumps(int[] _arr) {
266+
arr = _arr;
267+
n = arr.length;
268+
if (n == 1) return 0;
269+
for (int i = n - 1; i >= 0; i--) {
270+
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
271+
list.add(i);
272+
map.put(arr[i], list);
273+
}
274+
Deque<Integer> d1 = new ArrayDeque<>(), d2 = new ArrayDeque<>();
275+
int[] dist1 = new int[n], dist2 = new int[n];
276+
Arrays.fill(dist1, INF);
277+
Arrays.fill(dist2, INF);
278+
d1.addLast(0);
279+
dist1[0] = 0;
280+
d2.addLast(n - 1);
281+
dist2[n - 1] = 0;
282+
while (!d1.isEmpty() && !d2.isEmpty()) {
283+
int t = -1;
284+
if (d1.size() < d2.size()) t = update(d1, d2, dist1, dist2);
285+
else t = update(d2, d1, dist2, dist1);
286+
if (t != -1) return t;
287+
}
288+
return -1; // never
289+
}
290+
int update(Deque<Integer> d1, Deque<Integer> d2, int[] dist1, int[] dist2) {
291+
int t = d1.pollFirst(), step = dist1[t];
292+
if (t + 1 < n) {
293+
if (dist2[t + 1] != INF) return step + 1 + dist2[t + 1];
294+
if (dist1[t + 1] == INF) {
295+
d1.addLast(t + 1);
296+
dist1[t + 1] = step + 1;
297+
}
298+
}
299+
if (t - 1 >= 0) {
300+
if (dist2[t - 1] != INF) return step + 1 + dist2[t - 1];
301+
if (dist1[t - 1] == INF) {
302+
d1.addLast(t - 1);
303+
dist1[t - 1] = step + 1;
304+
}
305+
}
306+
List<Integer> list = map.getOrDefault(arr[t], new ArrayList<>());
307+
for (int ne : list) {
308+
if (dist2[ne] != INF) return step + 1 + dist2[ne];
309+
if (dist1[ne] == INF) {
310+
d1.addLast(ne);
311+
dist1[ne] = step + 1;
312+
}
313+
}
314+
map.remove(arr[t]);
315+
return -1;
316+
}
317+
}
318+
```
319+
* 时间复杂度:$O(n)$
320+
* 空间复杂度:$O(n)$
321+
322+
---
323+
324+
### 其他「图论搜索 / 模拟」内容
325+
326+
题太简单?不如来学习热乎的 [简单图论搜索题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247490011&idx=1&sn=4c8cbd5ad858f571291d47fcef75e75b&chksm=fd9cb2c4caeb3bd2ac442b2d4d1417e8eb6d65b1feca8399179951ebfa132e8a97a3935e7498&token=252055586&lang=zh_CN#rd) 🍭🍭🍭
327+
328+
* [常规 BFS(二维转一维)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489621&idx=1&sn=5d43fb97bc167a50a7aeb4ae2068571c&chksm=fd9cb34acaeb3a5c7e1e2e2a88d460ae2418a3cef615e1abf017b5d58aa1e7f490856d67f800&token=2136593799&lang=zh_CN#rd)
329+
* [常规 BFS/迭代加深(结合二叉树)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489746&idx=2&sn=9e80b33c12e96369c7a770382a97adbb&chksm=fd9cb3cdcaeb3adb35c708e548851e419b00e41801c98cae146ba29f5bdc49370a43cddf668d&token=252055586&lang=zh_CN#rd)
330+
* [多源 BFS](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247487179&idx=1&sn=e30a662c03fba3861254dbcf3fb9d6f2&chksm=fd9ca5d4caeb2cc205804fd17a2ce86b25d0408adc3417e73154f59d37e7cb17e02374f5122c&scene=178&cur_album_id=1917113998693449732#rd)
331+
* [双向 BFS](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489502&idx=1&sn=dc863d4bc71c4739a4799b9a4558bd01&chksm=fd9cbcc1caeb35d749d0d72f485485527482c27b608c8f4062c29a997ede97a09ce598b58c7f&scene=178&cur_album_id=1917113998693449732#rd)
332+
* [双向 BFS II](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247486981&idx=1&sn=045ea6c880080fea1ce807794ccff69b&chksm=fd9ca51acaeb2c0c83d13e3b2a5196895d1a1b44f8981cc3efad9d6a2af158267010646cc262&scene=178&cur_album_id=1917113998693449732#rd)
333+
* [双向 BFS III(结合并查集)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489671&idx=1&sn=c0f64de1a5e4613675f73d2ae43d0708&chksm=fd9cb398caeb3a8eae334c89dee17711fca43a00d93cf63a623792f3aac0c8bf586b4be9cc47&token=2074150457&lang=zh_CN#rd)
334+
* [灵活运用多种搜索方式(启发式)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489560&idx=2&sn=bb966d868c18d656620a20d31a425b23&chksm=fd9cb307caeb3a11424428f0a88e7f0cb86bb53b3e5a2b9e28683a24bcb3ac151655d2b6419e&scene=178&cur_album_id=1917113998693449732#rd)
335+
* [灵活运用多种搜索方式 II(启发式)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489588&idx=1&sn=479e4c0627247ab7e20af7909f2a8b64&chksm=fd9cb32bcaeb3a3d4f0bd73f023a92a165edabf212af1db9672a55bed1af7d4e32e8af9964c3&scene=178&cur_album_id=1917113998693449732#rd)
336+
* [灵活运用多种搜索方式 III(启发式 结合状态压缩)](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489985&idx=1&sn=e503ce6ece048062f1d9ebee2572838a&chksm=fd9cb2decaeb3bc8c635c4a6cf0e78d5973723bb6c89a64875828435dc5b90ef07874ef7a6ae&token=252055586&lang=zh_CN#rd)
337+
338+
---
339+
340+
### 最后
341+
342+
这是我们「刷穿 LeetCode」系列文章的第 `No.1345` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
343+
344+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
345+
346+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
347+
348+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
349+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /