55. Jump Game (M)

本文探讨了Jump Game算法问题,旨在判断玩家是否能从游戏的起始位置到达终点。通过三种方法——回溯法、动态规划和贪心算法,详细解析了如何优化算法以提高效率。回溯法虽直观但效率低下,动态规划提供了更优解,而贪心算法则实现了时间和空间的双重优化。

Jump Game (M)

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

题意

给定一个数组,每个数字元素代表以当前下标为起点,能够向右走的步数,判断该数组能否从第一个元素开始走到最后一个元素。

思路

使用回溯法会超时,原因在于对每个下标进行了多次判断。利用数组记录每个下标在递归过程中是否已经尝试过,以此对回溯法优化,可以AC,但运行时间将近1000ms。

动态规划:dp[i]代表以下标i结尾的左子数组整体能够走到的最远的下标。以 [3, 1, 2, 0] 为例,dp[0] = 3 表示子数组 [3] 最远能走到下标3,dp[1] = 3 表示子数组 [3, 1] 整体最远能走到下标3,dp[2] = 4 表示子数组 [3, 1, 2] 整体最远能走到下标4(虽然4已经在数组外)。不难发现规律:dp[i]=max(dp[i−1], i+nums[i])dp[i]=max(dp[i-1],\ i+nums[i])dp[i]=max(dp[i1], i+nums[i]),当然前提是 i <= dp[i - 1],即i必须是一个能够到达的点;如果不能到达i,则i之后所有的下标都无法到达,那么最后一个下标也一定无法到达。

官方解答(贪心):官方提供了一种更加高效省空间的贪心算法:用lastPos标记最左侧一个能够到达尾元素的下标,初始为nums.length - 1;从右向左扫描,如果当前下标i能够到达的最远位置 i + nums[i] >= lastPos,说明从i出发能够到达尾元素,更新 lastPos = i。最后判断 lastPos == 0。


代码实现 - 动态规划

class Solution {
    public boolean canJump(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            // 只有当i能到达才更新dp[i];i如果无法到达,则nums.length-1一定也无法到达
            if (i <= dp[i - 1]) {
                dp[i] = Math.max(dp[i - 1], i + nums[i]);
                // 如果判断已经可以到达最后一个下标,则提前结束
                if (dp[i] >= nums.length - 1) {
                    return true;
                }
            } else {
                return false;
            }
        }
        return true;
    }
}

代码实现 - 官方解答(贪心)

class Solution {
    public boolean canJump(int[] nums) {
        int lastPos = nums.length - 1;
        for (int i = nums.length - 2; i >= 0; i--) {
            if (i + nums[i] >= lastPos) {
                lastPos = i;
            }
        }
        return lastPos == 0;
    }
}

代码实现 - 回溯法

class Solution {
    boolean[] tried;

    public boolean canJump(int[] nums) {
        tried = new boolean[nums.length];
        return jump(nums, 0);
    }

    private boolean jump(int[] nums, int index) {
        if (index >= nums.length - 1) {
            return true;
        }

        for (int i = index + 1; i <= index + nums[index]; i++) {
            // 如果下标i已经尝试过,说明i走不通,无需再尝试
            if (tried[i]) {
                continue;
            }
            if (jump(nums, i)) {
                return true;
            }
            // 每次行不通则将i标记为已尝试
            tried[i] = true;
        }

        return false;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值