【LeetCode】跳跃游戏 (dp/贪心)

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

分析:

方法1:回溯法

模拟从第一个位置跳到最后位置的所有方案,从第一个位置开始,模拟所有可以跳到的位置,然后从当前位置重复上述操作,当没有办法继续跳的时候,就回溯

时间复杂度:O(2^n),每个位置都有跳和不跳两种选择,需要遍历所有选择

空间复杂度:O(N),递归栈的开销

 

方法2:回溯+备忘录法(自低向上)

当我们确定一个坐标可以跳到最后位置的时候,结果就不会改变了,这意味着我们可以记录这个结果,每次不用重新计算

时间复杂度:O(N*N)

空间复杂度:O(2*N)=O(N),第一个N为递归栈的开销,第二个N为备忘录的开销

 

方法3:自底向上的动态规划

我们把可以到达最后位置的点叫做好点,否则叫做坏点

自低向上可以消除递归栈的开销

从后往前遍历,当前点的位置i+此时可以走的步数j(>=1&&j<=len,len为此时可以走的最多步数)=k,如果k没有越界并且k是一个好点,那么可以推导出i也是一个好点,最后我们只需要确认起始0点是不是一个好点就可以了

时间复杂度:O(N*N)

空间复杂度:O(N)

class Solution {
public:
bool canJump(vector<int>& v)
{
    int n=v.size();
    int dp[n];
    memset(dp,0,sizeof(dp));
    dp[n-1]=1;
    for(int i=n-2;i>=0;i--)//从后往前
    {
        int len=v[i];//此时可以走的最大步数
        for(int j=1;j<=len;j++)
        {
            int k=i+j;//位置k
            if(k>=n)//k越界
                break;
            if(dp[k]==1)//k是一个好点,那么i肯定也是一个好点
            {
                dp[i]=1;
                break;
            }
        }
    }
    if(dp[0]==1)//最后只需要判断起始0点是不是一个好点
        return true;
    return false;
}
};

 

方法四:贪心法

我们发现每次都是在k点的左边寻找一个好点,那么如果当前位置i+当前位置可以走的步数v[i]>=上一个好点的位置(i右边第一个好点的位置),那么则i也是一个好点,最后确认一个最后得到的好点的位置是不是起始0点即可

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
bool canJump(vector<int>& v)
{
    int n=v.size();
    int pre=n-1;//上一个好点的位置
    for(int i=n-1;i>=0;i--)
    {
        if(i+v[i]>=pre)//i点可以到达上一个好点,那么i点也是好点
            pre=i;//上一个好点位置设置为i点
    }
    return pre==0;//最后只需要判断最后得到的好点是不是起始0点即可
}
};

 

 

posted @ 2019-10-14 11:11  西*风  阅读(214)  评论(0编辑  收藏  举报