Python面试宝典第16题:跳跃游戏

题目

        给你一个非负整数数组 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),只需要常数级别的额外空间来存储几个变量。

        可以看到,在处理大数据集时,贪心+最远可达更新算法在效率上明显优于动态规划法。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-22 12:14:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-22 12:14:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-22 12:14:03       45 阅读
  4. Python语言-面向对象

    2024-07-22 12:14:03       55 阅读

热门阅读

  1. JVM的内存空间划分

    2024-07-22 12:14:03       17 阅读
  2. Dell Idrac9New服务器监控指标解读

    2024-07-22 12:14:03       18 阅读
  3. 每日一题~ abc363()

    2024-07-22 12:14:03       16 阅读
  4. UE 反射

    2024-07-22 12:14:03       16 阅读
  5. 【普及动规】dp例题精讲+强化练习

    2024-07-22 12:14:03       17 阅读
  6. String/StringBuffer/StringBuilder 区别(详解)

    2024-07-22 12:14:03       17 阅读
  7. 排序规则utf8_general_ci的作用是什么?

    2024-07-22 12:14:03       12 阅读