题目
给你一个非负整数数组 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 <= nums.length <= 10^4
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类,,,时间爆炸了。
整体思路是通过递归尝试所有可能的跳跃步数,逐步跳跃,检查是否能到达数组的最后一个下标。
拆解分析
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
函数的解析:
- 该函数通过递归尝试从当前位置跳跃到下一个位置。
- 每次跳跃时,会更新当前位置,并检查是否到达数组末尾或者可以通过一步跳到数组末尾。
- 如果在当前位置无法一步跳到末尾,会尝试所有可能的跳跃步数,寻找能到达最远位置的跳跃。
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
到达或超过末尾,则可以到达末尾。
拆解分析
- 主循环
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)
。