Medium

题目描述

给你一个非负整数数组 arr,你最初位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]

请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。

注意,不能跳出数组外。

示例 1:

输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 5 -> 下标 4 -> 下标 1 -> 下标 3 
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3 

示例 2:

输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true 
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 0 -> 下标 4 -> 下标 1 -> 下标 3

示例 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

解题思路

这是一个典型的图遍历问题,可以用 BFS 或 DFS 来解决。

核心思路: 从起始位置 start 开始,我们可以向左跳(i - arr[i])或向右跳(i + arr[i])。每次跳跃后检查新位置是否有效(在数组范围内且未访问过),如果新位置的值为 0,则返回 true。

解法对比:

  1. BFS(推荐):使用队列进行层序遍历,能够找到最短路径,逻辑清晰
  2. DFS:使用递归或栈进行深度优先搜索,代码更简洁但可能栈溢出

BFS 实现步骤:

  • 使用队列存储可达位置和访问数组避免重复访问
  • 从起始位置开始,计算两个可能的跳跃位置
  • 检查新位置是否在数组范围内且未被访问
  • 如果新位置值为 0 则返回 true,否则加入队列继续搜索
  • 队列为空时仍未找到则返回 false

时间复杂度为 O(n),空间复杂度为 O(n),其中 n 为数组长度。

代码实现

class Solution {
public:
    bool canReach(vector<int>& arr, int start) {
        int n = arr.size();
        vector<bool> visited(n, false);
        queue<int> q;
        
        q.push(start);
        visited[start] = true;
        
        while (!q.empty()) {
            int curr = q.front();
            q.pop();
            
            if (arr[curr] == 0) {
                return true;
            }
            
            // 向右跳
            int right = curr + arr[curr];
            if (right < n && !visited[right]) {
                visited[right] = true;
                q.push(right);
            }
            
            // 向左跳
            int left = curr - arr[curr];
            if (left >= 0 && !visited[left]) {
                visited[left] = true;
                q.push(left);
            }
        }
        
        return false;
    }
};
class Solution:
    def canReach(self, arr: List[int], start: int) -> bool:
        n = len(arr)
        visited = [False] * n
        queue = deque([start])
        visited[start] = True
        
        while queue:
            curr = queue.popleft()
            
            if arr[curr] == 0:
                return True
            
            # 向右跳
            right = curr + arr[curr]
            if right < n and not visited[right]:
                visited[right] = True
                queue.append(right)
            
            # 向左跳
            left = curr - arr[curr]
            if left >= 0 and not visited[left]:
                visited[left] = True
                queue.append(left)
        
        return False
public class Solution {
    public bool CanReach(int[] arr, int start) {
        int n = arr.Length;
        bool[] visited = new bool[n];
        Queue<int> queue = new Queue<int>();
        
        queue.Enqueue(start);
        visited[start] = true;
        
        while (queue.Count > 0) {
            int curr = queue.Dequeue();
            
            if (arr[curr] == 0) {
                return true;
            }
            
            // 向右跳
            int right = curr + arr[curr];
            if (right < n && !visited[right]) {
                visited[right] = true;
                queue.Enqueue(right);
            }
            
            // 向左跳
            int left = curr - arr[curr];
            if (left >= 0 && !visited[left]) {
                visited[left] = true;
                queue.Enqueue(left);
            }
        }
        
        return false;
    }
}
var canReach = function(arr, start) {
    const n = arr.length;
    const visited = new Array(n).fill(false);
    const queue = [start];
    visited[start] = true;
    
    while (queue.length > 0) {
        const curr = queue.shift();
        
        if (arr[curr]

复杂度分析

复杂度类型复杂度说明
时间复杂度O(n)每个位置最多访问一次,n 为数组长度
空间复杂度O(n)需要 visited 数组和队列存储,最坏情况下队列大小为 n

相关题目