题目
给你一个非负整数数组 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,所以永远不可能到达最后一个下标。
动态规划法
使用动态规划法求解本题的基本思路为:从数组的末尾开始向前计算,看是否能够从某个位置跳到最终位置。具体而言,我们定义一个布尔型数组dp,其中dp[i]表示是否可以从位置 i 跳跃到达数组的最后一个位置。根据题目要求,我们的目标是确定dp[0]是否为True。使用动态规划法求解本题的主要步骤如下。
1、初始化。由于最后一个位置不需要跳跃即可到达,所以 dp[-1] = True。
2、从后向前遍历。对于数组中的每个位置 i, 从倒数第二个位置开始向前遍历至第一个位置,检查每个位置 i 能否通过它所能到达的下一个位置 j(满足 j + nums[j] >= i)间接到达终点。如果存在这样的 j 且 dp[j] 为 True,则设置 dp[i] = True。
3、结果判断。遍历完成后,dp[0] 的值即为所求。如果为 True,说明可以从第一个位置跳到最后一个位置,反之则不能。
根据上面的算法步骤,我们可以得出下面的示例代码。
def jump_game_by_dp(nums):
n = len(nums)
# 初始化 dp 数组,最后一个位置设为 True
dp = [False] * n
dp[n - 1] = True
# 从倒数第二个位置开始向前遍历
for i in range(n - 2, -1, -1):
# 检查当前位置能否通过后续位置跳转到达终点
max_reach = min(i + nums[i], n - 1)
for j in range(i + 1, max_reach + 1):
# 如果后续某个位置可达终点,则当前位置也可达
if dp[j]:
dp[i] = True
break
return dp[0]
nums = [2, 3, 1, 1, 4]
print(jump_game_by_dp(nums))
nums = [3, 2, 1, 0, 4]
print(jump_game_by_dp(nums))
贪心算法
本题单纯采用贪心算法,并不能求得最优解。但可以采用贪心+最远可达更新算法,其基本思想是:维护一个变量来记录当前能到达的最远位置,然后逐步推进,确保每一步都能保持在“最远可达”范围内,直到覆盖整个数组或发现无法到达更远的地方。使用贪心+最远可达更新算法求解本题的主要步骤如下。
1、初始化。设置两个变量:maxReach初始化为第一个元素的值,表示当前能跳到的最远位置;curEnd初始化也为第一个元素的值,表示当前遍历的最远边界。
2、遍历与更新。遍历数组,对于每个位置 i,如果 i 在curEnd之内,尝试更新maxReach为当前位置 i 加上其数值nums[i]和当前maxReach中的较大值。这样,maxReach一直保持为当前位置可达的最远边界。当 i 等于curEnd时,说明已经探索完当前可达区域,此时将curEnd更新为maxReach的值,继续探索。
3、判断结果。如果在遍历过程中,maxReach能够超过或到达数组的最后一个索引,说明可以到达终点。否则,不能到达。
根据上面的算法步骤,我们可以得出下面的示例代码。
def jump_game_greedy(nums):
n = len(nums)
# 初始化最远可达位置和当前边界
maxReach = curEnd = 0
for i in range(n):
# 如果当前位置超过了当前边界,无法继续前进
if i > curEnd:
return False
# 更新最远可达位置
maxReach = max(maxReach, i + nums[i])
# 当前边界已经达到或超过了最远可达位置,移动边界
if i == curEnd:
curEnd = maxReach
# 如果最远可达位置能够覆盖最后一个元素,说明可以到达终点
return True
nums = [2, 3, 1, 1, 4]
print(jump_game_greedy(nums))
nums = [3, 2, 1, 0, 4]
print(jump_game_greedy(nums))
总结
动态规划法的时间复杂度为O(n^2),实际上并不高效,特别是内部还有一个额外的循环来检查可达性。其空间复杂度为O(n),因为需要一个额外的数组来存储每个位置是否可达的信息。
贪心+最远可达更新算法的效率较高,因为它避免了重复计算,每次迭代都确保了下一步至少有一个可跳到的位置,并且始终尝试最大化下一步的跳跃范围。其时间复杂度为O(n),每个元素只被访问一次。空间复杂度为O(1),只需要常数级别的额外空间来存储几个变量。
可以看到,在处理大数据集时,贪心+最远可达更新算法在效率上明显优于动态规划法。