LeetCode 每日一题笔记
0. 前言
- 日期:2026.05.18
- 题目:1345. 跳跃游戏 IV
- 难度:困难
- 标签:数组、广度优先搜索、哈希表
1. 题目理解
问题描述 :
给定一个整数数组 arr,你从下标 0 开始。每一步可以从下标 i 跳到:
i + 1(需满足i + 1 < arr.length)i - 1(需满足i - 1 >= 0)- 任意
j(需满足arr[j] == arr[i]且j != i)
求到达数组最后一个下标的最少操作次数。
示例:
输入:
arr = [100,-23,-23,404,100,23,23,23,3,404]输出:
3解释:最优路径:
0 → 4 → 3 → 9,共3次跳跃。
输入:arr = [7]输出:
0解释:初始位置就是终点,无需跳跃。
2. 解题思路
核心观察
- 这是典型的无权图最短路径问题,BFS是天然的最优解法;
- 同值跳跃会产生大量边,若不优化会导致超时;
- 关键优化:处理完一个值的所有下标后,立即从哈希表移除该值,避免重复处理。
算法步骤
- 预处理哈希表:建立「值 → 对应下标列表」的映射;
- BFS初始化:队列加入起点 0,标记已访问;
- 逐层遍历队列,每层对应一步操作;
- 对当前节点,依次处理:
- 右邻居
i+1 - 左邻居
i-1 - 所有同值下标
- 右邻居
- 处理完同值下标后,从哈希表移除该值;
- 到达终点时立即返回当前步数。
3. 代码实现
java
package lc1345;
import java.util.*;
class Solution {
int bfs(HashMap<Integer, List<Integer>> map, int[] arr) {
int res = 0;
int last = arr.length - 1;
boolean[] visited = new boolean[arr.length];
Queue<Integer> queue = new LinkedList();
queue.offer(0);
while (!queue.isEmpty()) {
int m = queue.size();
for (int i = 0; i < m; i++) {
int n = queue.poll();
visited[n] = true;
if (n == last) {
return res;
}
if (n < last && !visited[n + 1]) {
visited[n + 1] = true;
queue.offer(n + 1);
}
if (n > 0 && !visited[n - 1]) {
visited[n - 1] = true;
queue.offer(n - 1);
}
if (map.containsKey(arr[n])) {
List<Integer> list = map.get(arr[n]);
for (int num : list) {
if (num != n && !visited[num]) {
visited[num] = true;
queue.offer(num);
}
}
map.remove(arr[n]);
}
}
res += 1;
}
return -1;
}
public int minJumps(int[] arr) {
HashMap<Integer, List<Integer>> map = new HashMap<>();
for (int i = arr.length - 1; i >= 0; i--) {
map.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
}
return bfs(map, arr);
}
}
4. 代码优化说明
减少if分支判断,合并重复逻辑,使用更高效的队列实现:
java
package lc1345;
import java.util.*;
class Solution {
public int minJumps(int[] arr) {
int n = arr.length;
if (n == 1) return 0;
HashMap<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < n; i++) {
map.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
}
boolean[] visited = new boolean[n];
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(0);
visited[0] = true;
int step = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int curr = queue.poll();
if (curr == n - 1) return step;
// 处理左右邻居
if (curr + 1 < n && !visited[curr + 1]) {
visited[curr + 1] = true;
queue.offer(curr + 1);
}
if (curr - 1 >= 0 && !visited[curr - 1]) {
visited[curr - 1] = true;
queue.offer(curr - 1);
}
// 处理同值下标
if (map.containsKey(arr[curr])) {
for (int next : map.get(arr[curr])) {
if (!visited[next]) {
visited[next] = true;
queue.offer(next);
}
}
map.remove(arr[curr]);
}
}
step++;
}
return -1;
}
}
5. 复杂度分析
-
时间复杂度 :O(n)O(n)O(n)
- 每个下标最多入队一次,每个值最多处理一次;
- 哈希表移除操作避免了重复处理同值下标,是不超时的关键。
-
空间复杂度 :O(n)O(n)O(n)
- 哈希表、队列、访问数组的空间开销均为线性级。
6. 总结
- 核心思路:BFS 求最短路径 + 哈希表批量处理同值跳跃;
- 关键优化:处理完一个值的所有下标后立即从哈希表移除,避免重复入队;
- 本题是 BFS 在数组跳跃问题中的经典应用,重点考察对时间复杂度的优化能力。