zoukankan      html  css  js  c++  java
  • Medium | LeetCode 1306. 跳跃游戏 III | 深度优先遍历(递归) | 广度优先遍历

    1306. 跳跃游戏 III

    这里有一个非负整数数组 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

    解题思路

    方法一: 递归(深度优先搜索)

    递归判断往前跳arr[start]步和往后跳arr[start]步是否能到达目的地。这种办法当前在LeetCode发生了栈溢出。

    public boolean canReach(int[] arr, int start) {
        if (arr[start] == 0) {
            return true;
        }
        boolean canReachDes = false;
        int posPost, posPre;
        // 尝试往前跳, 看是否能跳成功
        if ((posPost = start + arr[start]) < arr.length) {
            canReachDes = canReach(arr, posPost);
        }
        if (canReachDes) {
            return true;
        }
        // 尝试往后跳, 看是否能跳成功
        if ((posPre = start - arr[start]) >= 0) {
            return canReach(arr, posPre);
        }
        return false;
    }
    

    上述代码是一段有可能产生死循环的代码。例如在数组[1, 1, 2, 0]中, 如果下标从0处开始, 会一直沿着下标 0 -> 1 -> 2 -> 0 - > 1 -> 2....的路径死循环下去。

    解决死循环的问题很简单, 只需要把访问过的路径记住。如果下次又回到了之前访问过的节点, 那么就知道是发生死循环了, 直接返回false

    private static int UNKNOWN_CODE = 0;
    private static int REACH_CODE = 1;
    private static int UNREACH_CODE = 2;
    private int[] canReach;
    private boolean[] visit;
    
    public boolean canReach(int[] arr, int start) {
        this.canReach = new int[arr.length];
        this.visit = new boolean[arr.length];
        return canReachToZero(arr, start);
    }
    
    public boolean canReachToZero(int[] arr, int start) {
        if (arr[start] == 0) {
            return true;
        }
        // 记忆化数组
        if (canReach[start] != UNKNOWN_CODE) {
            return canReach[start] == 1;
        }
        // 看当前节点是否之前访问过, 如果访问过则发生了死循环, 直接返回false
        if (!visit[start]) {
            visit[start] = true;
        } else {
            return false;
        }
        boolean canReachDes = false;
        int posPost, posPre;
        // 往后找, 看是否能到达
        if ((posPost = start + arr[start]) < arr.length) {
            canReachDes = canReachToZero(arr, posPost);
        }
        if (canReachDes) {
            return true;
        }
        // 往前找, 看是否能到达
        if ((posPre = start - arr[start]) >= 0) {
            canReachDes =  canReachToZero(arr, posPre);
        }
        // 记录并且返回当前的结果
        canReach[start] = canReachDes ? REACH_CODE : UNREACH_CODE;
        return canReachDes;
    }
    

    方法二: 非递归(广度优先搜索)

    public boolean canReach2(int[] arr, int start) {
        if (arr[start] == 0) {
            return true;
        }
    
        int n = arr.length;
        boolean[] used = new boolean[n];
        Deque<Integer> q = new ArrayDeque<>();;
        q.push(start);
        used[start] = true;
    
        while (!q.isEmpty()) {
            int u = q.peekFirst();
            q.pop();
            if (u + arr[u] < n && !used[u + arr[u]]) {
                // 往右计算, 没有访问过
                if (arr[u + arr[u]] == 0) {
                    return true;
                }
                // 就把右边的这个节点加入队列当中
                q.push(u + arr[u]);
                // 设置右边这个节点已经防卫过
                used[u + arr[u]] = true;
            }
            if (u - arr[u] >= 0 && !used[u - arr[u]]) {
                // 同时对左边的节点做同样的操作
                if (arr[u - arr[u]] == 0) {
                    return true;
                }
                q.push(u - arr[u]);
                used[u - arr[u]] = true;
            }
        }
        return false;
    }
    
  • 相关阅读:
    【JavaScript】关于javascript原型的深入理解
    【JavaScript】关于JS中的constructor与prototype
    【JavaScript】关于prototype
    【JavaScript】重温Javascript继承机制
    【JavaScript】新浪微博ajax请求后改变地址栏url,但页面不跳转的方案解析
    【JavaScript】JavaScript函数的参数
    【JavaScript】页面加载性能优化
    HTML5 修改浏览器url而不刷新页面
    【339】matplotlib based on python3
    【338】Pandas.DataFrame
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14590946.html
Copyright © 2011-2022 走看看