题目描述
这里有一个非负整数数组 arr
,你最开始位于该数组的起始下标 start
处。当你位于下标 i
处时,你可以跳到 i + arr[i]
或者 i - arr[i]
。
请你判断自己是否能够跳到对应元素值为 0
的 任意 下标处。
注意,不管是什么情况下,你都无法跳到数组之外。
样例
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 0 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [3,0,2,1,2], start = 2
输出:false
解释:无法到达值为 0 的下标 1 处。
限制
1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length
算法1
(深度优先遍历 / DFS) $O(n)$
- 从起点开始深度优先遍历,遍历过的结点不再遍历。
- 遍历的函数设为布尔返回值,如果当前结点为 0,则返回
true
。如果两种选择的返回结果都是false
,则返回false
。
时间复杂度
- 每个点仅遍历一次,故时间复杂度为 $O(n)$。
空间复杂度
- 递归最坏需要额外 $O(n)$ 的系统栈空间。
C++ 代码
class Solution {
public:
bool dfs(int x, const vector<int> &arr, vector<bool> vis) {
if (arr[x] == 0)
return true;
if (x + arr[x] < arr.size() && !vis[x + arr[x]]) {
vis[x + arr[x]] = true;
if (dfs(x + arr[x], arr, vis))
return true;
}
if (x - arr[x] >= 0 && !vis[x - arr[x]]) {
vis[x - arr[x]] = true;
if (dfs(x - arr[x], arr, vis))
return true;
}
return false;
}
bool canReach(vector<int>& arr, int start) {
vector<bool> vis(arr.size(), false);
vis[start] = true;
return dfs(start, arr, vis);
}
};
算法2
(宽度优先遍历 / BFS) $O(n)$
- 从起点开始宽度优先遍历,每个结点仅遍历一次,如果当前结点为 0,则返回
true
。
时间复杂度
- 每个点仅遍历一次,故时间复杂度为 $O(n)$。
空间复杂度
- 队列最坏需要额外 $O(n)$ 的空间。
C++ 代码
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
queue<int> q;
vector<bool> vis(arr.size(), false);
vis[start] = true;
q.push(start);
while (!q.empty()) {
int x = q.front();
q.pop();
if (arr[x] == 0)
return true;
if (x + arr[x] < arr.size() && !vis[x + arr[x]]) {
vis[x + arr[x]] = true;
q.push(x + arr[x]);
}
if (x - arr[x] >= 0 && !vis[x - arr[x]]) {
vis[x - arr[x]] = true;
q.push(x - arr[x]);
}
}
return false;
}
};