LeetCode Top 100 Liked Questions 55. Jump Game (Java版; Medium)

welcome to my blog

LeetCode Top 100 Liked Questions 55. Jump Game (Java版; Medium)

题目描述
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.

//贪心
class Solution {
    public boolean canJump(int[] nums) {
        //dp[i]表示能否跳到i
        //dp[j]==true && j+nums[j]>=i     j=[0,i-1]
        int n = nums.length;
        int right = 0;
        for(int i=0; i<n; i++){
	        //如果i大于right, 那么说明没法走到i, 直接返回false; 必须满足i<=right
            if(i>right){
                return false;
            }
            right = Math.max(right, i+nums[i]);
        }
        return right>=n-1;
    }
}
//动态规划
class Solution {
    public boolean canJump(int[] nums) {
        //dp[i]表示能否跳到i
        //dp[j]==true && j+nums[j]>=i     j=[0,i-1]
        int n = nums.length;
        boolean[] dp = new boolean[n];
        dp[0] = true;
        for(int i=1; i<n; i++){
            for(int j=i-1; j>=0; j--){
                if(dp[j] && (j+nums[j]>=i)){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[n-1];
    }
}
第二次做; 动态规划; 核心: 1)dp[i]含义: 能否跳到i 2)递推式dp[i] = dp[i-1]&&nums[i]>=1 || dp[i-2]&&nums[i-2]>=2 || … || dp[0]&&nums[0]>=i
class Solution {
    public boolean canJump(int[] nums) {
        //
        int n = nums.length;
        /*
        dp[i]含义: 能否跳到i

        dp[i] = dp[i-1]&&nums[i]>=1 || dp[i-2]&&nums[i-2]>=2 || ... || dp[0]&&nums[0]>=i
        */
        boolean[] dp = new boolean[n];
        dp[0] = true;
        for(int i=1; i<n; i++){
            //j从大到小, 执行时间骤减到2ms
            for(int j=i-1; j>=0 && dp[i]==false; j--){
                dp[i] = dp[i] || dp[j] && nums[j] >= (i-j);
            }
        }
        return dp[n-1];

    }
}
第一次做, 贪心, 时间复杂度O(N), 2ms, 太快了, 秒杀之前的动态规划方法, 空间复杂度O(1); 贪心法: 自底向上(从右往左); 用left记录最左边的good的位置, 如果当前位置i能够够到left, 则当前位置i是新的最左边的good, 遍历结束后看看left是否为0, 是的话说明能从i=0到达最后一个位置, 返回true, 不是的话返回false
class Solution {
    public enum State{
        Unknown, Good, Bad
    };
    State[] s;
    public boolean canJump(int[] nums) {
        if(nums==null || nums.length==0)
            return false;
        //贪心法: 自底向上(从右往左); 用left记录最左边的good的位置, 如果当前位置i能够够到left, 则当前位置i是新的最左边的good, 遍历结束后看看left是否为0, 是的话返回true, 不是的话返回false
        int left = nums.length-1;
        for(int i=nums.length-2; i>=0; i--){
            if(i+nums[i] >= left)
                left = i;
        }
        return left == 0;
    }

}
第一次做, 自底向上(从右往左),时间复杂度O(N^2), 555ms, 空间复杂度O(N)
自顶向下和自底向上动态规划的区别就是消除了回溯,在实际使用中,自底向上的方法有更好的时间效率,因为我们不再需要栈空间,可以节省很多缓存开销。更重要的是,
这可以让之后更有优化的空间。回溯通常是通过反转动态规划的步骤来实现的。 这是由于我们每次只会向右跳动,意味着如果我们从右边开始动态规划,每次查询右边节点的信息,
都是已经计算过了的,不再需要额外的递归开销,因为我们每次在 memo 表中都可以找到结果
class Solution {
    public enum State{
        Unknown, Good, Bad
    };
    State[] s;
    public boolean canJump(int[] nums) {
        if(nums==null || nums.length==0)
            return false;
        //来个自底向上的动态规划
        s = new State[nums.length];
        
        //自底向上的写法, 还能省去不少初始化操作
        //for(int i=0; i<nums.length-1; i++)
            //s[i] = State.Unknown;
        
        //最后一个位置肯定能到最后一个位置, 不动就行了
        s[nums.length-1] = State.Good;
        //自底向上(从右往左)
        for(int i=nums.length-2; i>=0; i--){
            for(int j=1; j<=nums[i]; j++){
                if(i+j < nums.length && s[i+j] == State.Good){
                    s[i] = State.Good;
                    break;
                }
                else{
                    s[i] = State.Bad;
                }
            }
        }
        return s[0] == State.Good;
    }

}
第一次做, 优化后的回溯法, 空间换时间, 时间复杂度O(N^2), 1210ms, 空间复杂度O(N); 自顶向下(从左往右); 注意什么时候让s[index]为State.Good, 什么时候为State.bad
/*
感觉像回溯法, 尝试各种可能; 但是最后超时了! 
使用空间换时间, 优化回溯法, 记录每个元素对应的状态: unknown, good, bad
good表示从该点能到达最后一个位置
bad表示不能从该点到达最后一个位置
unknown表示还不清楚处能否从该点到达最后一个位置
*/
class Solution {
    public enum State{
        Unknown, Good, Bad
    };
    State[] s;
    public boolean canJump(int[] nums) {
        if(nums==null || nums.length==0)
            return false;
        
        s = new State[nums.length];
        for(int i=0; i<nums.length; i++)
            s[i] = State.Unknown;
        s[nums.length-1] = State.Good;
        
        boolean res = Core(nums, 0);
        return res;
    }
    public boolean Core(int[] arr, int index){
        //base case
        if(index==arr.length-1)
            return true;
        if(index>arr.length-1)
            return false;
        //如果当前位置的好坏已知, 便不用再递归了,直接返回记录的结果即可(拿空间换时间), 减少大量重复计算
        if(s[index] != State.Unknown)
            return s[index] == State.Good;
        //
        boolean res = false;
        for(int i=arr[index]; i>=1; i--){
            res |= Core(arr, index+i);
            s[index] = res==true? State.Good : State.Bad;
            if(res == true)
                return true;
        }
        //here, 说明该点不能到达最后, 记录为Bad
        s[index] = State.Bad;
        return res;
    }
}
第一次做, 回溯法, 递归, 超时了; 第70个案例(共75个)没通过
/*
感觉像回溯法, 尝试各种可能; 但是最后超时了! 超时的例子
[8,2,4,4,4,9,5,2,5,8,8,0,8,6,9,1,1,6,3,5,1,2,6,6,0,4,8,6,0,3,2,8,7,6,5,1,7,0,3,4,8,3,5,9,0,4,0,1,0,5,9,2,0,7,0,2,1,0,8,2,5,1,2,3,9,7,4,7,0,0,1,8,5,6,7,5,1,9,9,3,5,0,7,5]
*/
class Solution {
    public boolean canJump(int[] nums) {
        if(nums==null || nums.length==0)
            return false;
        boolean res = Core(nums, 0);
        return res;
    }
    public boolean Core(int[] arr, int index){
        //base case
        if(index==arr.length-1)
            return true;
        if(index>arr.length-1)
            return false;
        //
        boolean res = false;
        for(int i=1; i<=arr[index]; i++){
            res |= Core(arr, index+i);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值