LeetCode | Jump Game II(跳跃游戏II)

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.

Your goal is to reach the last index in the minimum number of jumps.

For example:
Given array A = [2,3,1,1,4]

The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.)

题目解析:

要跳转到最后一个位置,至少要跳多少次。


方案一:动态规划(大数据超时)

我们可以利用动态规划,第i个元素能跳A[i]步。那么其跳转到最后结点的次数至少是step[i+1]….step[i+a[i] ]最小值+1。

这样就需要设一个step[n]数组来保存要至少跳转多少步,最后返回step[0]即可。

class Solution {
public:
    int jump(int A[], int n) {
        if(n <= 1)
            return 0;
        int *step = new int[n];
        step[n-1] = 0;
        for(int i = n-2;i >= 0;i--){
            if(A[i] + i >= n-1){
                step[i] = 1;
            }else{
                int min = step[i+1];
                for(int j = 2;j <= A[i];j++){
                    if(min > step[i+j])
                        min = step[i+j];
                }
                step[i] = min + 1;
            }
        }
        return step[0];
    }
};

令人诧异的是,为什么有个网友写的动态规划却没超时?虽然时间复杂度O(n^2),但我的明显效率更高一些,我们有把i…n-1的全遍历。现在贴出来看看,有网友知道的话,麻烦告知一下。

class Solution {
public:
    int* dp;
    int jump(int A[], int n) {
        if(n==0)
        {
            return INT_MAX;
        }
        dp = new int[n];
        dp[0] = 0;
        for(int i=1;i<n;i++)
        {
            dp[i] = INT_MAX;
        }
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(j+A[j]>=i)
                {
                    int tmp = dp[j]+1;
                    if(tmp < dp[i])
                    {
                        dp[i] = tmp;
                        break;
                    }
                }
            }
        }
        
        return dp[n-1];
    }
};

方案二:

借助LeetCode | Jump Game方案二的思想。

当从前向后遍历的时候,不断更新最大值,当最大值大于n的时候就能保证跳转成功。那么我们如何借鉴呢?

我们用range来保存从第i点开始能跳转到第几个结点,那么在i….range范围内,我们只计算a[j]+j,来更新内部的一个max,表示这个范围内的数据能跳转到的最远索引值。只有在range…max中遍历时,才更新count。当max>=n时,证明从i…range中至少有一个位置j,可以一次跳转到终点。

class Solution {
public:
    int jump(int A[], int n) {
        if(n<=1)
            return 0;

        int count = 0;
        int range = A[0];
        for(int i = 0;i < n;){  //这里不能再i++,因为子过程中的while循环的i++已经达到了下次要循环的边界
            count++;
            if(range >= n-1)
                return count;
            //找到下次循环的最大位置
            int max = 0;
            while(i <= range){
                max = max > A[i]+i ? max : A[i]+i;
                i++;
            }
            range = max;
        }
        return count;
    }
};


同样的思路,网友的代码更好一些,但要仔细揣摩才能出来。

class Solution {
public:
    int jump(int A[], int n) {
       int jump = 0;
       int lastMax = 0;    //trace the max with jump times jump
       int curMax = 0;    //trace the max from 0~i

       for(int i = 0; i < n; i ++)
       {
           if(lastMax < i)
           {
                jump ++;            // need a jump!
                lastMax = curMax;    // jump to new max(curMax)
           }
           curMax = max(curMax, A[i]+i);
       }
       return jump;
    }
};

方案三:贪心算法

更精巧的方法,不过时间复杂度有点高,但思想很不错!先找到第一个能一口气跳到最后的,然后找哪一个能一口气跳转到该位置的,以此类推。

class Solution{
public:
    int jump(int A[], int n) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int count = 0;
        int end = n-1;
        while(end){
            for(int i = 0; i < end; i++){
                if((A[i]+i)>=end){
                    count++;
                    end = i;
                }
            }
        }
        return count;
    }
    
};




















点赞