LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】

简介: LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

给定一个非负整数数组 nums,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

输入格式
  • nums:一个由非负整数构成的数组。
输出格式
  • 返回一个布尔值,表示是否能到达最后一个位置。

示例

示例 1
输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从位置 0 到 1,然后从位置 1 跳 3 步到最后一个位置。
示例 2
输入: nums = [3,2,1,0,4]
输出: false
解释: 无论如何,你总会到达位置 3,但该位置的最大跳跃长度是 0,所以你永远不可能到达最后一个位置。

方法一:贪心算法

解题步骤
  1. 初始化最远距离max_reach 初始化为 0,用于记录能到达的最远位置。
  2. 遍历数组:遍历数组中的每个位置,并更新 max_reach
  3. 判断可达性:如果在某个位置 imax_reach < i,说明无法继续向前跳跃,返回 false;如果 max_reach 大于等于数组的最后一个位置,则返回 true
完整的规范代码
def canJump(nums):
    """
    使用贪心算法判断能否跳到最后一个位置
    :param nums: List[int], 输入的数组
    :return: bool, 是否能到达最后一个位置
    """
    max_reach, n = 0, len(nums)
    for i in range(n):
        if i > max_reach:
            return False
        max_reach = max(max_reach, i + nums[i])
        if max_reach >= n - 1:
            return True
    return False
# 示例调用
print(canJump([2,3,1,1,4]))  # 输出: True
print(canJump([3,2,1,0,4]))  # 输出: False
算法分析
  • 时间复杂度:(O(n)),其中 n 是数组 nums 的长度。
  • 空间复杂度:(O(1)),使用了常数额外空间。

方法二:回溯算法

解题步骤
  1. 定义回溯函数:尝试从当前位置开始,模拟所有可能的跳跃步数,向前递归探索。
  2. 递归跳跃:从当前位置起跳,逐步减小跳跃距离直到 0,递归调用自身。
  3. 终止条件:如果到达数组末尾,返回 true;如果所有跳跃尝试都失败,返回 false
完整的规范代码
def canJumpFromPosition(position, nums):
    if position == len(nums) - 1:
        return True
    furthest_jump = min(position + nums[position], len(nums) - 1)
    for next_position in range(position + 1, furthest_jump + 1):
        if canJumpFromPosition(next_position, nums):
            return True
    return False
def canJump(nums):
    """
    使用回溯算法判断能否跳到最后一个位置
    :param nums: List[int], 输入的数组
    :return: bool, 是否能到达最后一个位置
    """
    return canJumpFromPosition(0, nums)
# 示例调用
print(canJump([2,3,1,1,4]))  # 输出: True
print(canJump([3,2,1,0,4]))  # 输出: False
算法分析
  • 时间复杂度:(O(2^n)),其中 n 是数组 nums 的长度,每个位置都可能产生递归调用。
  • 空间复杂度:(O(n)),递归的深度最多为 n

方法三:动态规划(自底向上)

解题步骤
  1. 初始化状态数组dp[i] 表示从位置 i 开始是否能到达数组的末尾。
  2. 状态转移:从后向前填充 dp 数组,根据 dp[j] (对于所有 i < j <= i+nums[i]) 更新 dp[i]
  3. 结果返回:返回 dp[0]
完整的规范代码
def canJump(nums):
    """
    使用动态规划算法判断能否跳到最后一个位置
    :param nums: List[int], 输入的数组
    :return: bool, 是否能到达最后一个位置
    """
    n = len(nums)
    dp = [False] * n
    dp[n - 1] = True
    
    for i in range(n - 2, -1, -1):
        furthest_jump = min(i + nums[i], n - 1)
        for j in range(i + 1, furthest_jump + 1):
            if dp[j]:
                dp[i] = True
                break
    return dp[0]
# 示例调用
print(canJump([2,3,1,1,4]))  # 输出: True
print(canJump([3,2,1,0,4]))  # 输出: False
算法分析
  • 时间复杂度:(O(n^2)),其中 n 是数组 nums 的长度,对于每个元素,可能需要遍历 nums[i] 次。
  • 空间复杂度:(O(n)),存储状态数组 dp 需要 n 个额外空间。

方法四:优化的贪心算法

解题步骤
  1. 从后向前遍历:从数组的倒数第二个元素开始,向前遍历数组。
  2. 更新目标位置:如果当前位置加上可跳跃的最大长度大于或等于目标位置,则更新目标位置为当前位置。
  3. 结果判断:遍历结束后,如果目标位置更新为数组的起始位置,则返回 true,否则返回 false
完整的规范代码
def canJump(nums):
    """
    使用优化的贪心算法判断能否跳到最后一个位置
    :param nums: List[int], 输入的数组
    :return: bool, 是否能到达最后一个位置
    """
    n = len(nums)
    lastPos = n - 1  # 最初目标位置为数组的最后一个位置
    for i in range(n - 2, -1, -1):  # 从倒数第二个位置向前遍历
        if i + nums[i] >= lastPos:  # 如果当前位置能跳到或跳过目标位置
            lastPos = i  # 更新目标位置为当前位置
    return lastPos == 0  # 最终目标位置是否为起始位置
# 示例调用
print(canJump([2,3,1,1,4]))  # 输出: True
print(canJump([3,2,1,0,4]))  # 输出: False
算法分析
  • 时间复杂度:(O(n)),其中 n 是数组 nums 的长度,只需遍历一次数组。
  • 空间复杂度:(O(1)),使用了常数的额外空间。

方法五:索引哈希映射

解题步骤
  1. 建立索引哈希映射:创建一个字典来记录每个元素能达到的最远距离。
  2. 判断可达性:从第一个元素开始,逐个检查每个元素是否可达,同时更新可达的最远距离。
  3. 递归结束条件:如果在某个点发现最远距离无法继续向前或已覆盖数组末尾,则停止。
完整的规范代码
def canJump(nums):
    """
    使用索引哈希映射法判断能否跳到最后一个位置
    :param nums: List[int], 输入的数组
    :return: bool, 是否能到达最后一个位置
    """
    max_reach = 0  # 初始化最远可达位置
    for i in range(len(nums)):
        if i > max_reach:
            return False  # 当前索引不可达
        max_reach = max(max_reach, i + nums[i])  # 更新最远可达位置
        if max_reach >= len(nums) - 1:
            return True  # 已可达数组末尾
    return False
# 示例调用
print(canJump([2,3,1,1,4]))  # 输出: True
print(canJump([3,2,1,0,4]))  # 输出: False
算法分析
  • 时间复杂度:(O(n)),其中 n 是数组 nums 的长度,只需遍历一次数组。
  • 空间复杂度:(O(1)),使用了常数的额外空间。

不同算法的优劣势对比

特征 方法一: 贪心算法 方法二: 回溯算法 方法三: 动态规划 方法四: 优化贪心 方法五: 索引哈希映射
时间复杂度 (O(n)) (O(2^n)) (O(n^2)) (O(n)) (O(n))
空间复杂度 (O(1)) (O(n)) (O(n)) (O(1)) (O(1))
优势 快速简单 理论上完备 结构清晰 更直观高效 直观简洁
劣势 基本无 极度低效 空间复杂 需逆向思维 与方法一类似

应用示例详解

跳跃游戏的算法在多个领域内都有广泛的应用,特别是在游戏开发、动画制作、机器学习等领域。以下是两个具体的应用场景,展示如何将跳跃游戏算法转化为实际可用的技术解决方案。

应用一:游戏开发中的关卡可玩性验证

在平台跳跃游戏的开发过程中,设计师常常需要创建出既具有挑战性又能确保玩家能够完成的关卡。使用跳跃游戏算法可以帮助开发者验证任何给定关卡的可玩性。

场景描述

  • 设计师创建了一个关卡,其中包含不同高度和间隔的平台,玩家从起点开始,需要通过跳跃到达终点。
  • 每个平台的数字表示玩家从该点最多可以向前跳跃的距离(即 nums 数组)。

技术实现

  • 开发者使用贪心算法,从起点开始,实时计算玩家可以到达的最远距离。
  • 如果在任一点,计算得到的最远距离小于该点的索引,意味着玩家无法从当前平台跳到下一个平台,关卡不可通行。

代码示例

def verify_level(nums):
    max_reach = 0
    for i, jump in enumerate(nums):
        if i > max_reach:
            return False, i  # 返回不可通行的最远点
        max_reach = max(max_reach, i + jump)
        if max_reach >= len(nums) - 1:
            return True, None  # 关卡可通行
    return False, len(nums) - 1
# 关卡设计示例:玩家可以从每个位置跳跃的最大长度
level_design = [2, 3, 1, 0, 4, 2, 1]
is_playable, fail_point = verify_level(level_design)
print(f"Level Playable: {is_playable}, Failure Point: {fail_point}")

输出解析

  • 如果关卡可通行,函数返回 TrueNone
  • 如果关卡不可通行,函数指出玩家被卡住的具体位置。
应用二:动画制作中的运动路径规划

动画制作中,制定角色或物体的移动路径是一个核心任务,跳跃游戏算法可以用来确保动画中的运动路径是连贯和逻辑上可行的。

场景描述

  • 动画师需要创建一个场景,其中一个角色需要从场景的一端跳跃到另一端。
  • 每个可能的着陆点的数字表示从该点角色能够跳跃的最大长度。

技术实现

  • 使用贪心算法来确定角色的每一步是否都能顺利落到一个合法的着陆点。
  • 这样的算法保证了动画的连贯性和视觉上的合理性。

代码示例

def plan_animation_path(spots):
    reach = 0
    for i in range(len(spots)):
        if i > reach:
            return False  # 角色无法继续前进
        reach = max(reach, i + spots[i])
    return True  # 角色可以顺利完成动画
# 动画路径设计示例:角色可以从每个位置跳跃的最大长度
animation_path = [1, 2, 0, 3, 2, 0, 1]
can_complete = plan_animation_path(animation_path)
print(f"Animation Path Complete: {can_complete}")

输出解析

  • 返回 True 表示角色可以顺利从起点跳到终点。
  • 返回 False 表示存在至少一个点使得角色无法从该点继续前进。

总结

通过上述应用示例,我们可以看到跳跃游戏算法不仅仅局限于理论问题,它可以广泛应用于实际项目中,如游戏开发和动画制作,帮助开发者和设计师验证和规划合理的路径和策略。这种算法的实际应用强调了线性代数在现代编程中的实用价值和广泛适用性。


欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
1天前
|
算法 Java
[Java·算法·简单] LeetCode 283. 移动零
[Java·算法·简单] LeetCode 283. 移动零
10 2
|
1天前
|
算法 Java
[Java·算法·中等] LeetCode21. 合并两个有序链表
[Java·算法·中等] LeetCode21. 合并两个有序链表
9 2
|
3天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
3天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
4天前
|
存储 算法 调度
力扣中级算法(Python)
力扣中级算法(Python)
|
3天前
|
算法 JavaScript 决策智能
基于禁忌搜索算法的TSP路径规划matlab仿真
**摘要:** 使用禁忌搜索算法解决旅行商问题(TSP),在MATLAB2022a中实现路径规划,显示优化曲线与路线图。TSP寻找最短城市访问路径,算法通过避免局部最优,利用禁忌列表不断调整顺序。关键步骤包括初始路径选择、邻域搜索、解评估、选择及禁忌列表更新。过程示意图展示搜索效果。
|
4天前
|
机器学习/深度学习 算法
基于BP神经网络和小波变换特征提取的烟草香型分类算法matlab仿真,分为浓香型,清香型和中间香型
```markdown 探索烟草香型分类:使用Matlab2022a中的BP神经网络结合小波变换。小波分析揭示香气成分的局部特征,降低维度,PCA等用于特征选择。BP网络随后处理这些特征,以区分浓香、清香和中间香型。 ```
|
5天前
|
机器学习/深度学习 算法
m基于PSO-GRU粒子群优化长门控循环单元网络的电力负荷数据预测算法matlab仿真
摘要: 在MATLAB 2022a中,对比了电力负荷预测算法优化前后的效果。优化前为&quot;Ttttttt111222&quot;,优化后为&quot;Tttttttt333444&quot;,明显改进体现为&quot;Tttttttttt5555&quot;。该算法结合了粒子群优化(PSO)和长门控循环单元(GRU)网络,利用PSO优化GRU的超参数,提升预测准确性和稳定性。PSO模仿鸟群行为寻找最优解,而GRU通过更新门和重置门处理长期依赖问题。核心MATLAB程序展示了训练和预测过程,包括使用&#39;adam&#39;优化器和超参数调整,最终评估并保存预测结果。
15 0
|
6天前
|
算法 安全
基于龙格库塔算法的SIR病毒扩散预测matlab仿真
该程序使用龙格库塔算法实现SIR模型预测病毒扩散,输出易感、感染和康复人群曲线。在MATLAB2022a中运行显示预测结果。核心代码设置时间区间、参数,并定义微分方程组,通过Runge-Kutta方法求解。SIR模型描述三类人群动态变化,常微分方程组刻画相互转化。模型用于预测疫情趋势,支持公共卫生决策,但也存在局限性,如忽略空间结构和人口异质性。
|
6天前
|
机器学习/深度学习 监控 算法
基于yolov2深度学习网络的昆虫检测算法matlab仿真,并输出昆虫数量和大小判决
YOLOv2算法应用于昆虫检测,提供实时高效的方法识别和定位图像中的昆虫,提升检测精度。核心是统一检测网络,预测边界框和类别概率。通过预测框尺寸估算昆虫大小,适用于农业监控、生态研究等领域。在matlab2022A上运行,经过关键升级,如采用更优网络结构和损失函数,保证速度与精度。持续优化可增强对不同昆虫的检测能力。![image.png](https://ucc.alicdn.com/pic/developer-ecology/3tnl7rfrqv6tw_e760ff6682a3420cb4e24d1e48b10a2e.png)