55. 跳跃游戏

题目

给你一个非负整数数组 nums ,你最初位于数组的第一个下标。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true;否则,返回 false

示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

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

提示:

  1. 1 <= nums.length <= 10^4
  2. 0 <= nums[i] <= 10^5

代码

完整代码

#include <stdbool.h>

// 定义一个递归函数 jump,用来尝试从当前下标跳跃到不同的位置,返回能够到达的最远下标
int jump(int* nums, int numsSize, int MaxjumpStep, int* nowIndex, int JumpStep)
{
    (*nowIndex) += JumpStep; // 当前下标更新为跳跃后的下标
    if((*nowIndex) == numsSize - 1) // 如果到达了数组末尾,返回数组末尾下标
    {
        return numsSize - 1;
    }
    if((*nowIndex) + nums[(*nowIndex)] >= numsSize - 1) // 如果当前位置可以一步跳到数组末尾,返回数组末尾下标
    {
        return numsSize - 1;
    }
    int furthestCanReach = *nowIndex; // 当前能够到达的最远位置
    int thisTurnReach = *nowIndex; // 本次跳跃能够到达的位置
    for (int i = nums[(*nowIndex)]; i > 0; i--) // 尝试从当前下标跳跃到不同的位置
    {
        thisTurnReach = jump(nums, numsSize, nums[(*nowIndex)], nowIndex, i);
        if(thisTurnReach > furthestCanReach) // 更新最远能够到达的位置
        {
            furthestCanReach = thisTurnReach;
        }
        if(furthestCanReach == numsSize - 1) // 如果能够到达数组末尾,返回最远能够到达的位置
        {
            return furthestCanReach;
        }
    }
    (*nowIndex) -= JumpStep; // 回退当前下标
    return furthestCanReach; // 返回最远能够到达的位置
}

// 主函数 canJump,判断是否可以跳到数组末尾
bool canJump(int* nums, int numsSize) {
    if(numsSize == 1) // 如果数组长度为1,直接返回true
    {
        return true;
    }
    if(nums[0] == 0) // 如果初始位置为0,直接返回false
    {
        return false;
    }
    int nowIndex = 0; // 当前下标初始化为0
    for (int i = 1; i <= nums[0]; i++) // 尝试从初始位置跳跃到不同的位置
    {
        if(numsSize - 1 == jump(nums, numsSize, nums[0], &nowIndex, i)) // 调用jump函数,判断是否能够到达数组末尾
        {
            return true;
        }
    }
    return false; // 如果无法到达数组末尾,返回false
}

思路分析

这套代码用了递归的方法。
只是为了练练dfs类,,,时间爆炸了。
整体思路是通过递归尝试所有可能的跳跃步数,逐步跳跃,检查是否能到达数组的最后一个下标。

拆解分析

  1. jump函数
int jump(int* nums, int numsSize, int MaxjumpStep, int* nowIndex, int JumpStep)
{
    (*nowIndex) += JumpStep;
    if((*nowIndex) == numsSize - 1) // 到达末尾
    {
        return numsSize - 1;
    }
    if((*nowIndex) + nums[(*nowIndex)] >= numsSize - 1) // 可以一步跳过去
    {
        return numsSize - 1;
    }
    int furthestCanReach = *nowIndex;
    int thisTurnReach = *nowIndex;
    for (int i = nums[(*nowIndex)]; i > 0; i--)
    {
        thisTurnReach = jump(nums, numsSize, nums[(*nowIndex)], nowIndex, i);
        if(thisTurnReach > furthestCanReach)
        {
            furthestCanReach = thisTurnReach;
        }
        if(furthestCanReach == numsSize - 1)
        {
            return furthestCanReach;
        }
    }
    (*nowIndex) -= JumpStep;
    return furthestCanReach;
}

jump函数的解析:

  • 该函数通过递归尝试从当前位置跳跃到下一个位置。
  • 每次跳跃时,会更新当前位置,并检查是否到达数组末尾或者可以通过一步跳到数组末尾。
  • 如果在当前位置无法一步跳到末尾,会尝试所有可能的跳跃步数,寻找能到达最远位置的跳跃。
  1. canJump函数
bool canJump(int* nums, int numsSize) {
    if(numsSize == 1)
    {
        return true;
    }
    if(nums[0] == 0)
    {
        return false;
    }
    int nowIndex = 0;
    for (int i = 1; i <= nums[0]; i++)
    {
        if(numsSize - 1 == jump(nums, numsSize, nums[0], &nowIndex, i))
        {
            return true;
        }
    }
    return false;
}

canJump函数的解析:

  • 该函数是主函数,负责初始化并调用jump函数。
  • 首先检查数组长度为1的特殊情况,然后检查初始位置是否为0。
  • 通过遍历从初始位置能跳跃的所有步数,调用jump函数,判断是否能到达数组末尾。

复杂度分析

  • 时间复杂度:由于代码使用了递归,且在每一步都尝试所有可能的跳跃步数,因此时间复杂度为O(2^n),其中n是数组长度。
  • 空间复杂度:递归调用栈的深度决定了空间复杂度,为O(n)

一题多解

贪心算法

完整代码

#include <stdbool.h>

bool canJump(int* nums, int numsSize) {
    int furthest = 0; // 当前能到达的最远位置
    for (int i = 0; i < numsSize; i++) {
        if (i > furthest) { // 如果当前位置超过了最远能到达的位置,返回false
            return false;
        }
        // 更新最远能到达的位置
        furthest = (i + nums[i] > furthest) ? i + nums[i] : furthest;
        if (furthest >= numsSize - 1) { // 如果最远能到达的位置超过或等于数组末尾,返回true
            return true;
        }
    }
    return false; // 无法到达数组末尾,返回false
}

思路分析

用了贪心算法方法。

整体思路是维护一个变量furthest,记录当前能够到达的最远位置。如果遍历过程中某个位置超过了当前能够到达的最远位置,则不能到达末尾;如果能够更新furthest到达或超过末尾,则可以到达末尾。

拆解分析

  1. 主循环
for (int i = 0; i < numsSize; i++) {
    if (i > furthest) {
        return false;
    }
    furthest = (i + nums[i] > furthest) ? i + nums[i] : furthest;
    if (furthest >= numsSize - 1) {
        return true;
    }
}

主循环的解析:

  • 遍历数组中的每一个位置,检查是否能到达该位置。
  • 如果当前的位置i超过了furthest,说明无法到达当前位置,返回false
  • 更新furthest为当前位置能够跳到的最远位置。
  • 如果furthest到达或超过数组末尾,返回true

复杂度分析

  • 时间复杂度:遍历一次数组,时间复杂度为O(n)
  • 空间复杂度:仅使用了常数个额外空间,空间复杂度为O(1)

结果

DFS迭代:

在这里插入图片描述

贪心:

在这里插入图片描述

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值