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 <= nums.length <= 104
0 <= nums[i] <= 105
贪心算法
- 整体目标:
- 判断从数组的第一个下标出发,能否通过按照数组中每个位置所规定的最大跳跃长度进行跳跃,最终到达最后一个下标。
- 贪心策略:
- 在每一个位置,我们不需要去考虑所有可能的跳跃路径,而是只关注当前位置能够跳到的最远位置
furthest
。 - 对于当前位置
i
,我们可以计算出它能跳到的最远位置为i + nums[i]
- 不断更新最远距离
furthest
- 判断是否到达终点
- 在每一个位置,我们不需要去考虑所有可能的跳跃路径,而是只关注当前位置能够跳到的最远位置
- 贪心策略判断:
- 如果遍历过程中,遍历到某位置
i
时,i
超越了能跳到的最远位置furthest
。说明无法继续前进,不可能到达末尾 - 如果遍历整个数组,一直没出现以上情况,说明可以到达末尾!
- 如果遍历过程中,遍历到某位置
代码实现
根据以上思路很容易写出代码:
class Solution:def canJump(self, nums: List[int]) -> bool:# 贪心算法?furthest = 0 # 初始化最远距离# 从头开始遍历ifor i in range(len(nums)):furthest = max(i + nums[i], furthest)if i > furthest:return Falsereturn True
————————————————————————————————————
解答错误
107 / 173 个通过的测试用例官方题解
输入
nums =
[3,2,1,0,4]添加到测试用例
输出
true
预期结果
false
哪里出了问题?
思考:
if i > furthest:return False
实际上,应该加上>=
解答错误
152 / 173 个通过的测试用例官方题解
输入
nums =
[0]添加到测试用例
输出
false
预期结果
true
忘记考虑空集了!
class Solution:def canJump(self, nums: List[int]) -> bool:# 贪心算法?if not nums:return Falsefurthest = nums[0] # 初始化最远距离# 从头开始遍历ifor i in range(len(nums)):furthest = max(i + nums[i], furthest)if i >= furthest:return Falsereturn True
————————————————————————————————————————————————
解答错误
152 / 173 个通过的测试用例官方题解
输入
nums =
[0]添加到测试用例
输出
false
预期结果
true
通过用例完全没加…
正确答案:
def canJump(nums):n = len(nums)furthest = 0for i in range(n):if i > furthest:return Falsefurthest = max(furthest, i + nums[i])return True
这几个疑问:
疑问一:为什么先判断 if i > furthest:
再执行 furthest = max(furthest, i + nums[i])
?
在使用贪心算法解决“跳跃游戏”问题的这段代码中,furthest
变量用于记录从起始位置能够到达的最远位置。
-
先判断能否继续前进:当我们遍历数组
nums
时,在每一个位置i
,我们首先需要判断当前位置是否已经超出了之前所能到达的最远位置(即i > furthest
)。如果已经超出了,那就意味着按照之前的跳跃规则,我们无法到达当前位置i
,更不可能到达数组的最后一个下标了,所以此时应该直接返回False
,表示无法完成跳跃到达最后一个下标。 -
更新最远可到达位置:只有当当前位置
i
没有超出之前记录的最远位置furthest
时,我们才继续更新furthest
的值。通过furthest = max(furthest, i + nums[i])
,我们将当前位置i
加上它能跳跃的最大长度nums[i]
得到的新的可能到达的最远位置,与之前记录的最远位置furthest
进行比较,取两者中的最大值作为新的furthest
。这样就能不断更新我们能够到达的最远位置信息,以便后续继续判断是否能到达最后一个下标。
如果先更新 furthest
再判断 i > furthest
,就可能会出现一种情况:在更新 furthest
之后,当前位置 i
实际上已经是无法到达的了(因为是基于之前错误地认为能到达而更新的 furthest
),但却没有及时发现并返回 False
,导致后续的计算出现错误,最终得到错误的结果。所以必须先判断能否继续前进,再更新最远可到达位置。
疑问二:为什么 if i > furthest:
没有等于号=
?
这里不包含等于号是因为当 i == furthest
时,我们仍然有可能从当前位置继续跳跃到达更远的地方。
假设当前位置 i
刚好等于之前记录的最远位置 furthest
,这意味着我们刚好到达了之前所能到达的最远距离,但此时我们还可以根据当前位置 i
对应的元素 nums[i]
所表示的跳跃长度进行跳跃,有可能跳到更远的位置,所以此时不能判定为无法到达后续位置,不能返回 False
。
只有当 i
严格大于 furthest
时,才说明我们已经无法按照之前的跳跃规则到达当前位置 i
了,也就不可能到达最后一个下标,此时才返回 False
。
疑问三:为什么不用写特解情况,比如 nums
为空或者只有一个元素等!
-
对于空数组情况:在Python中,如果直接对空数组执行
for i in range(n)
(其中n
是数组长度)这样的操作,实际上循环体不会被执行。因为空数组的长度为0
,range(0)
会生成一个空的可迭代对象,所以循环内的代码不会被执行到。而在这种情况下,由于没有进行任何有效的跳跃操作,默认就可以认为是无法到达最后一个下标(因为根本就没有下标可供到达),所以最终函数会返回一个默认值(在Python中函数如果没有显式返回值,默认返回None
,这里由于代码结构,实际上返回值应该是根据循环执行情况确定的,循环未执行就相当于返回了False
的效果),这与我们期望的空数组时返回False
的结果是相符的。 -
对于只有一个元素的数组情况:当数组只有一个元素时,我们最初位于数组的第一个下标(也就是唯一的这个下标),此时不需要进行任何跳跃就已经到达了“最后一个下标”(因为只有一个下标)。在代码中,当进入循环时,
i
的值为0
,此时furthest
初始值为0
(假设没有在循环外对其进行额外赋值),那么i == furthest
,根据前面的分析,这种情况不会返回False
,而是会继续执行furthest = max(furthest, i + nums[i])
,更新后的furthest
依然为0
(因为nums[0]
加上0
还是0
),然后循环结束,最终函数会返回True
,这也符合我们对于只有一个元素的数组能到达最后一个下标(其实就是它本身)的预期结果。
所以在这段代码的逻辑下,不需要额外针对 nums
为空或者只有一个元素等特殊情况单独编写处理代码,代码本身的执行逻辑已经能够正确处理这些特殊情况并得到符合预期的结果。