题目描述
这里有一个非负整数数组arr
,你最开始位于该数组的起始下标start
处。当你位于下标1
处时,你可以跳到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
解题思路
对于本题,无论是使用广度优先还是深度优先都可以得到很好的解决,鉴于官方已经提供广度优先的题解,故本文给出对应的深度优先模板。
相较于传统的图遍历题目,本体在图的连接关系上稍作改动。既不是矩阵类的相邻单元具有连接,也没有给出邻接矩阵或邻接链表,需要根据跳跃规则,理清各结点的连接关系。
从start
结点开始深度遍历,方法的返回值类型为bool
,退出递归条件:1. 访问重复结点立即返回;2. 访问元素值为0
的结点返回true。每一层递归逻辑,标记当前结点已访问,递归遍历能跳跃的结点。
代码实现
通过邻接链表显示表示各结点的连接关系,
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
//构建graph,邻接链表
int n = arr.size();
vector<vector<int>> graph(n);
for(int i = 0; i < n; i++){
if(arr[i] == 0) continue;
if(arr[i] + i < n) graph[i].push_back(arr[i] + i);
if(i - arr[i] >= 0) graph[i].push_back(i - arr[i]);
}
return dfs(graph, arr, start);
}
bool dfs(vector<vector<int>>& graph, vector<int>& arr, int start){
if(arr[start] == -1) return false;
if(arr[start] == 0) return true;
bool res = false;
arr[start] = -1;
for(int i : graph[start]){
res |= dfs(graph, arr, i);
}
return res;
}
};
运行结果:
这惨不忍睹的performance,初步猜想是构建graph影响的。
优化掉构建邻接链表的过程,基于跳跃规则利用原arr[]
,大幅提高性能与内存开销。
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
//判断重复访问
if(arr[start] == -1) return false;
//抵达目标结点,return true
if(arr[start] == 0) return true;
bool res = false;
int step = arr[start];
//标记当前start已访问
arr[start] = -1;
//只能向左/右跳跃,i = 0时左跳,i = 1右跳。
for(int i = 0; i < 2; i++){
int temp = start + pow(-1, i) * step;
if(temp >= 0 && temp < arr.size()){
// 与运算符,只要能够访问到目标结点即为true!
res |= canReach(arr, temp);
}
}
return res;
}
};
运行结果: