跳跃游戏 III 图解题解
这道题到底在问什么
- 输入
- arr=[4,2,3,0,3,1,2], start=5
- 输出
- true(5→4→1→3,arr[3]=0)
- 输入
- arr=[3,0,2,1,2], start=2
- 输出
- false(怎么跳都落不到值为 0 的下标)
最优解:一步一步想明白
- 3记住「下标当节点、i±arr[i] 是两条边、seen 防重、BFS 找值为 0 的下标」,下面逐帧套它。
- 4从起点下标 5(紫)出发:标记为已访问(蓝)、放进队列。目标是落到某个值为 0 的下标——这张数组里 arr[3]=0,就是我们要找的终点。
- 5队首弹出下标 5(紫),值 arr[5] = 1,不是 0。看它能往两个方向跳:向右 5 + 1,向左 5 − 1。
- 6先算向右的落点:5 + 1 = 6。再看下标 6 能不能落脚。
- 7下标 6 在界内、还没访问过:标记为已访问(蓝),放进队列队尾,等轮到它再展开它的两个跳法。
- 8先算向左的落点:5 − 1 = 4。再看下标 4 能不能落脚。
- 9下标 4 在界内、还没访问过:标记为已访问(蓝),放进队列队尾,等轮到它再展开它的两个跳法。
- 10队首弹出下标 6(紫),值 arr[6] = 2,不是 0。看它能往两个方向跳:向右 6 + 2,向左 6 − 2。
- 11先算向右的落点:6 + 2 = 8。再看下标 8 能不能落脚。
- 12下标 8 超出了数组范围 0 到 6,这一跳跳出界了,放弃这个方向。
- 13先算向左的落点:6 − 2 = 4。再看下标 4 能不能落脚。
- 14下标 4(蓝)之前已经访问过了,BFS 不重复入队、直接跳过 —— 这正是 seen 数组的作用:挡住「跳过去又跳回来」的死循环。
- 15队首弹出下标 4(紫),值 arr[4] = 3,不是 0。看它能往两个方向跳:向右 4 + 3,向左 4 − 3。
- 16先算向右的落点:4 + 3 = 7。再看下标 7 能不能落脚。
- 17下标 7 超出了数组范围 0 到 6,这一跳跳出界了,放弃这个方向。
- 18先算向左的落点:4 − 3 = 1。再看下标 1 能不能落脚。
- 19下标 1 在界内、还没访问过:标记为已访问(蓝),放进队列队尾,等轮到它再展开它的两个跳法。
- 20队首弹出下标 1(紫),值 arr[1] = 2,不是 0。看它能往两个方向跳:向右 1 + 2,向左 1 − 2。
- 21先算向右的落点:1 + 2 = 3。再看下标 3 能不能落脚。
- 22下标 3 在界内、还没访问过:标记为已访问(蓝),放进队列队尾,等轮到它再展开它的两个跳法。
- 23先算向左的落点:1 − 2 = -1。再看下标 -1 能不能落脚。
- 24下标 -1 超出了数组范围 0 到 6,这一跳跳出界了,放弃这个方向。
- 25队首弹出下标 3(绿):它的值 arr[3] = 0 —— 命中目标!BFS 成功跳到了值为 0 的下标,答案 true。
⚠️ 容易写错的地方
✗ 错:不用 seen 数组防重
✓ 对:每个下标访问前先查 seen、访问后置位
i 跳到 j、j 又能跳回 i,不防重就会在两点间反复横跳、死循环
✗ 错:只往右跳、漏了 i − arr[i]
✓ 对:两个方向 i+arr[i] 和 i−arr[i] 都要试
少一条边会漏掉真正能到 0 的路径,误判 false
✗ 错:把「到达值为 0 的下标」当成「到达下标 0」
✓ 对:判的是 arr[i] == 0,不是 i == 0
题目要落到「值」为 0 的格子,和下标 0 没关系
完整代码(Python / C++ / Java)
Python
from typing import List
from collections import deque
class Solution:
def canReach(self, arr: List[int], start: int) -> bool:
n = len(arr)
seen = [False] * n
q = deque([start])
seen[start] = True
while q:
i = q.popleft()
if arr[i] == 0:
return True
for j in (i + arr[i], i - arr[i]):
if 0 <= j < n and not seen[j]:
seen[j] = True
q.append(j)
return FalseC++
#include <queue>
#include <vector>
using namespace std;
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
int n = arr.size();
vector<int> seen(n);
queue<int> q;
q.push(start); seen[start] = 1;
while (!q.empty()) {
int i = q.front(); q.pop();
if (arr[i] == 0) return true;
for (int j : {i + arr[i], i - arr[i]}) if (j >= 0 && j < n && !seen[j]) {
seen[j] = 1; q.push(j);
}
}
return false;
}
};Java
import java.util.*;
class Solution {
public boolean canReach(int[] arr, int start) {
boolean[] seen = new boolean[arr.length];
Queue<Integer> q = new ArrayDeque<>();
q.offer(start); seen[start] = true;
while (!q.isEmpty()) {
int i = q.poll();
if (arr[i] == 0) return true;
int a = i + arr[i], b = i - arr[i];
if (a >= 0 && a < arr.length && !seen[a]) { seen[a] = true; q.offer(a); }
if (b >= 0 && b < arr.length && !seen[b]) { seen[b] = true; q.offer(b); }
}
return false;
}
}复杂度
时间
O(n)
每个下标最多入队一次(seen 挡住重复),出队后只做常数次工作(看两个落点),共 O(n)
空间
O(n)
seen 数组 O(n),队列最坏也装 O(n) 个下标
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 跳跃游戏 III 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题用 BFS 还是 DFS?有区别吗?+
两种都行、复杂度都是 O(n)。本题只问「能不能到达」一个值为 0 的下标,不要求最少跳几次,所以 BFS(队列)和 DFS(递归或显式栈)效果等价。区别只在遍历顺序:BFS 一层层扩散,DFS 一条路走到底再回头;判可达性时随便选一个。
如果改成「最少跳几次才能到值为 0 的下标」呢?+
那就必须用 BFS 按层计数:每弹出一层把步数加一,第一次遇到值为 0 的下标时的层数就是最少步数。DFS 不保证先找到的就是最短路,需要额外记录并取最小,不如 BFS 自然。这也是 BFS 处理「等权最短路」的经典优势。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 跳跃游戏 III 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。