路径规划的艺术:不同路径 II 的算法深掘【python力扣63题】

news/2024/5/9 9:23:01

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例

题目描述

一个机器人位于一个 m x n 网格的左上角(起始点在下图标记为“Start”)。

机器人每次只能向下或向右移动一步。机器人试图达到网格的右下角(在下图标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 10 来表示。

输入格式
  • obstacleGrid:一个二维数组,表示网格,1 表示有障碍物,0 表示无障碍。
输出格式
  • 返回一个整数,表示所有可能的路径数量。

示例

示例 1
输入: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2
输入: obstacleGrid = [[0,1],[0,0]]
输出: 1

方法一:动态规划

解题步骤
  1. 初始化状态数组:创建一个二维数组 dp,其中 dp[i][j] 表示到达点 (i, j) 的路径数量。
  2. 处理障碍物:如果 obstacleGrid[i][j]1,则 dp[i][j] 设置为 0
  3. 初始化边界条件:对于第一行和第一列,如果某处有障碍物,则该处及其后的所有点都不可达,即设置为 0
  4. 状态转移:对于其他位置,路径数 dp[i][j] 等于从左边来的路径数加上从上面来的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1](前提是该位置无障碍物)。
  5. 返回结果dp[m-1][n-1] 即为所求结果。
完整的规范代码
def uniquePathsWithObstacles(obstacleGrid):"""使用动态规划解决带障碍物的不同路径问题:param obstacleGrid: List[List[int]], 网格,1表示障碍物:return: int, 不同的路径数量"""if obstacleGrid[0][0] == 1:return 0m, n = len(obstacleGrid), len(obstacleGrid[0])dp = [[0] * n for _ in range(m)]dp[0][0] = 1for i in range(1, m):dp[i][0] = dp[i-1][0] if obstacleGrid[i][0] == 0 else 0for j in range(1, n):dp[0][j] = dp[0][j-1] if obstacleGrid[0][j] == 0 else 0for i in range(1, m):for j in range(1, n):if obstacleGrid[i][j] == 0:dp[i][j] = dp[i-1][j] + dp[i][j-1]else:dp[i][j] = 0return dp[m-1][n-1]# 示例调用
print(uniquePathsWithObstacles([[0,0,0],[0,1,0],[0,0,0]]))  # 输出: 2
print(uniquePathsWithObstacles([[0,1],[0,0]]))  # 输出: 1
算法分析
  • 时间复杂度:(O(m * n)),需要填充一个 mn 列的矩阵。
  • 空间复杂度:(O(m * n)),使用了一个同样大小的二维数组作为动态规划表。

方法二:空间优化的动态规划

解题步骤
  1. 使用一维数组:利用一维数组 dp 来保存上一行的结果,降低空间复杂度。
  2. 迭代更新:对每一行使用相同的数组进行迭代更新,dp[j] 代表当前行第 j 列的路径数,更新公式仍为 dp[j] = dp[j] + dp[j-1],但需要考虑障碍物的影响。
  3. 初始化dp 的所有元素初始化为 0,并设置第一个元素为 1(如果起点无障碍物)。
完整的规范代码
def uniquePathsWithObstacles(obstacleGrid):"""使用一维数组进行动态规划,空间优化版本:param obstacleGrid: List[List[int]], 网格,1表示障碍物:return: int, 不同的路径数量"""n = len(obstacleGrid[0])dp = [0] * ndp[0] = 1 if obstacleGrid[0][0] == 0 else 0for row in obstacleGrid:for j in range(n):if row[j] == 1:dp[j] = 0elif j > 0:dp[j] += dp[j - 1]return dp[-1]# 示例调用
print(uniquePathsWithObstacles([[0,0,0],[0,1,0],[0,0,0]]))  # 输出: 2
print(uniquePathsWithObstacles([[0,1],[0,0]]))  # 输出: 1
算法分析
  • 时间复杂度:(O(m * n)),需要迭代更新数组 m 次,每次迭代有 n 步。
  • 空间复杂度:(O(n)),使用了一个长度为 n 的一维数组。### 方法三:数学组合方法(考虑障碍)
解题步骤
  1. 特殊情况处理:如果起点或终点有障碍物,直接返回 0。
  2. 动态规划组合数:利用组合数学的方法来计算路径,但由于存在障碍,此方法需要结合动态规划来计算可达的路径数。
完整的规范代码
def uniquePathsWithObstacles(obstacleGrid):"""结合动态规划的组合数学方法处理障碍物问题:param obstacleGrid: List[List[int]], 网格,1表示障碍物:return: int, 不同的路径数量"""m, n = len(obstacleGrid), len(obstacleGrid[0])if obstacleGrid[0][0] == 1 or obstacleGrid[m-1][n-1] == 1:return 0dp = [0] * ndp[0] = 1for i in range(m):for j in range(n):if obstacleGrid[i][j] == 1:dp[j] = 0elif j > 0:dp[j] += dp[j-1]return dp[-1]# 示例调用
print(uniquePathsWithObstacles([[0,0,0],[0,1,0],[0,0,0]]))  # 输出: 2
print(uniquePathsWithObstacles([[0,1],[0,0]]))  # 输出: 1
算法分析
  • 时间复杂度:(O(m * n)),每个格子被遍历一次。
  • 空间复杂度:(O(n)),使用了一维数组来保存当前行的状态。

方法四:深度优先搜索(DFS)

解题步骤
  1. DFS递归:从起点开始,递归地探索所有向右和向下的路径。
  2. 障碍物检查:如果遇到障碍物,停止在该方向的探索。
  3. 使用记忆化:使用哈希表来存储已计算的位置,避免重复计算。
完整的规范代码
def uniquePathsWithObstacles(obstacleGrid):"""使用DFS和记忆化搜索解决带障碍物的不同路径问题:param obstacleGrid: List[List[int]], 网格,1表示障碍物:return: int, 不同的路径数量"""m, n = len(obstacleGrid), len(obstacleGrid[0])memo = {}def dfs(x, y):if (x, y) in memo:return memo[(x, y)]if x >= m or y >= n or obstacleGrid[x][y] == 1:return 0if x == m-1 and y == n-1:return 1memo[(x, y)] = dfs(x+1, y) + dfs(x, y+1)return memo[(x, y)]return dfs(0, 0)# 示例调用
print(uniquePathsWithObstacles([[0,0,0],[0,1,0],[0,0,0]]))  # 输出: 2
print(uniquePathsWithObstacles([[0,1],[0,0]]))  # 输出: 1
算法分析
  • 时间复杂度:(O(m * n)),每个格子至多被访问一次。
  • 空间复杂度:(O(m * n)),递归栈的深度以及记忆化所需的空间。

方法五:广度优先搜索(BFS)

解题步骤
  1. 使用队列:利用队列进行层次遍历,从起点开始探索所有可达的点。
  2. 累加路径数:在队列中存储每个点的路径数量,到达新点时累加。
  3. 障碍物处理:遇到障碍物则跳过。
完整的规范代码
from collections import dequedef uniquePathsWithObstacles(obstacleGrid):"""使用BFS解决带障碍物的不同路径问题:param obstacleGrid: List[List[int]], 网格,1表示障碍物:return: int, 不同的路径数量"""if obstacleGrid[0][0] == 1:return 0m, n = len(obstacleGrid), len(obstacleGrid[0])queue = deque([(0, 0, 1)])  # (x, y, path_count)paths = [[0] * n for _ in range(m)]paths[0][0] = 1while queue:x, y, path_count = queue.popleft()for dx, dy in [(1, 0), (0, 1)]:nx, ny = x + dx, y + dyif 0 <= nx < m and 0 <= ny < n and obstacleGrid[nx][ny] == 0:if paths[nx][ny] == 0:queue.append((nx, ny, paths[x][y]))paths[nx][ny] += path_countreturn paths[m-1][n-1]# 示例调用
print(uniquePathsWithObstacles([[0,0,0],[0,1,0],[0,0,0]]))  # 输出: 2
print(uniquePathsWithObstacles([[0,1],[0,0]]))  # 输出: 1
算法分析
  • 时间复杂度:(O(m * n)),每个节点最多入队出队一次。
  • 空间复杂度:(O(m * n)),队列和路径计数数组的空间需求。

不同算法的优劣势对比

特征方法一: 动态规划方法二: 空间优化DP方法三: 数学组合方法四: DFS方法五: BFS
时间复杂度(O(m * n))(O(m * n))(O(m * n))(O(m * n))(O(m * n))
空间复杂度(O(m * n))(O(n))(O(1))(O(m * n))(O(m * n))
优势直观,易理解空间效率高计算最快,非迭代灵活,适用于复杂边界层次清晰,适用于大规模
劣势空间占用高优化限于列对大数处理有限制时间空间成本高需要额外存储空间

应用示例

游戏开发中的路径发现
在策略游戏或迷宫游戏中,开发者可以利用这些算法来计算从起点到终点的所有可能路径,为游戏的AI决策提供支持,比如在自动生成的迷宫中计算最优路径或在战略游戏中规划单位的行动路线。这些算法提供了不同的效率和实现复杂度,使得开发者可以根据具体游戏场景和性能要求选择最适合的方法。


http://www.mrgr.cn/p/34128087

相关文章

C++教学——从入门到精通 11.嵌套循环及数组

上次讲到了循环&#xff0c;这次来讲嵌套循环 如果一个人叫你用C来画一个10*10/2cm^2三角形会么&#xff1f; 这就要用到嵌套循环了 来看看结构&#xff1a; for(变量类型1 变量;条件1;返回值1){语句1;for(变量类型 变量2;条件2;返回值2){语句2;}语句3; } 语句1,2,3是依次…

视频滚动字幕一键批量轻松添加,解锁高效字幕编辑,提升视频质量与观众体验

视频已成为我们获取信息、娱乐休闲的重要渠道。一部成功的视频作品&#xff0c;除了画面精美、音质清晰外&#xff0c;字幕的添加也是至关重要的一环。字幕不仅能增强视频的观感&#xff0c;还能提升信息的传达效率&#xff0c;让观众在享受视觉盛宴的同时&#xff0c;更加深入…

【排课小工具】项目需求的搜集与整合

在小学实习期间(2024年3月1日 - 2024年7月10日),与老师的交流中发现,每当新学期开始都要人工排一次课表,并且这个过程较为繁琐,总是遇到教师课程冲突的状况,一旦发生这种情况,在重排的过程中就会影响到诸多已经排好的项目。如果能够解决上述排课冲突问题,那将会给排课…

实验14-1使用cnn完成MNIST手写体识别(tf)+实验14-2使用cnn完成MNIST手写体识别(keras)

版本python3.7 tensorflow版本为tensorflow-gpu版本2.6 实验14-1使用cnn完成MNIST手写体识别(tf)运行结果: 代码:import tensorflow as tf # Tensorflow提供了一个类来处理MNIST数据 from tensorflow.examples.tutorials.mnist import input_data import time# 载入数据集 mn…

ZYNQ之嵌入式开发04——自定义IP核实现呼吸灯、固化程序

文章目录 自定义IP核——呼吸灯实验固化程序 自定义IP核——呼吸灯实验 Xilinx官方提供了很多IP核&#xff0c;在Vivado的IP Catalog中可以查看这些IP核&#xff0c;在构建自己复杂的系统时&#xff0c;只使用Xilinx官方的免费IP核一般满足不了设计的要求&#xff0c;因此很多…

Android Dalvik虚拟机JNI方法的注册过程分析

Dalvik虚拟机在调用一个成员函数的时候&#xff0c;如果发现该成员函数是一个JNI方法&#xff0c;那么就会直接跳到它的地址去执行。也就是说&#xff0c;JNI方法是直接在本地操作系统上执行的&#xff0c;而不是由Dalvik虚拟机解释器执行。由此也可看出&#xff0c;JNI方法是A…

STM32H750时钟频率和功耗以及RTC功能测试

STM32H750时钟频率和功耗和RTC功能测试 &#x1f4cc;相关篇《STM32H750片外QSPI启动配置简要》 ✨在使用STM32CubeMX修改STM32H750时钟树参数时&#xff0c;如果使用软件自动求解&#xff0c;这是一个非常耗时的操作&#xff0c;有时候还不一定成功&#xff0c;还是推荐使用手…

数据库中间件-He3Proxy

什么是数据库中间件? 随着互联网行业的蓬勃发展,业务访问量、数据量激增,传统数据库的单库、大表已成为业务发展的瓶颈,进而衍生出数据库主从实例、分库分表等方案,为减少数据库层变动对业务开发带来的复杂性,一种连接应用与数据库桥梁的工具孕育而生,即数据库中间件,它…

JAVA前端快速入门基础_javascript入门(01)

写在前面:本文用于快速学会简易的JS&#xff0c;仅做扫盲和参考作用 1.JS是什么 JavaScript是一门跨平台&#xff0c;面向对象的脚本语言(即不需要编译&#xff0c;可以直接通过浏览器进行解释)。JS和Java是两门完全不相同的语言&#xff0c;但是基础的语法是类似的 2.JS的引…

MySQL学习之explain

from之后的查询得到的表叫做衍生表,是临时表数据,生成临时表之后的数据是无法使用索引的,如果数据量大查询效率就会比较低,这就是查询要尽量少使用子查询这些临时表。explain详解 id: 表示查询序号,也可以表示优先级;当值都不一样的时候,值越大表示优先级越高,越先执行…

实验12-使用keras预训练模型完成猫狗识别

版本python3.7 tensorflow版本为tensorflow-gpu版本2.6 运行结果: 这里我用Gpu进行加速,训练一回9秒,如果不启用gpu,训练一回会很慢。 代码:#-*- codeing = utf-8 -*- #@Time : 2022/10/2 11:44 #@Author : 程浩 #@File : 猫狗识别.py #@Software: PyCharm import tensor…

KVM虚拟机迁移(静态)

1.查看虚拟机状态,确认关闭状态 virsh list --all 2.查看虚拟机文件位置 virsh domblklist zabbix3.导出配置文件并查看导出文件 virsh dumpxml zabbix > /root/zabbix.xml 4.把刚导出的配置文件传到目的服务制定路径(路径为虚拟机配置文件位置)scp zabbix.xml 10.10.7.1…

实验11-使用keras完成逻辑回归

版本python3.7 tensorflow版本为tensorflow-gpu版本2.6 运行结果: 代码:import numpy as npfrom keras.models import Sequential from keras.layers import Dense, Dropout, Activation, Flatten import matplotlib.pyplot as plt from sklearn import datasets# 样本数据集…

【工具-PyCharm】

工具-PyCharm ■ PyCharm-简介■ PyCharm-安装■ PyCharm-使用■ 修改主题■ 设置字体■ 代码模板■ 解释器配置■ 文件默认编码■ 快捷键■ 折叠■ 移动■ 注释■ 编辑■ 删除■ 查看■ 缩进■ 替换 ■ PyCharm-简介 官方下载地址 Professional&#xff1a;专业版&#xff0…

Linux KASAN使用与实现原理

一、KASAN工具使用 KASAN工具&#xff1a;Kernel Address SANitizer(KASAN)是一种动态内存安全错误检测工具&#xff0c;主要功能是检查内存越界访问和使用已释放内存的问题。 1.1 KASAN宏控开关 KASAN有三种模式&#xff1a;1.通用KASAN&#xff1b;2.基于软件标签的KASAN&…

实验8-1tensorboard可视化+实验8-2tensorboard案例

版本python3.7 tensorflow版本为tensorflow-gpu版本2.6 实验8-1tensorboard可视化运行结果:代码:import tensorflow as tf# 创建默认图 graph = tf.compat.v1.get_default_graph()# 定义命名空间 with graph.as_default():with tf.name_scope(input):# fetch:就是同时运行多…

科技论文网站:中国科技论文在线

文章目录 1. Intro2. Main3. Cons Evaluation彩蛋&#xff1a;科学素质 这是作者最后一次发 这种类型的宣传 科普文章 1. Intro 中国科技论文在线是经教育部批准&#xff0c;由教育部科技发展中心主办&#xff0c; 利用现代信息技术手段&#xff0c;打破传统出版物的概念&…

虚方法

若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法。虚方法与非虚方法的最大不同是,虚方法的实现可以由派生类所取代,这种取代是通过方法的重写实现的(以后再讲)虚方法的特点:虚方法前不允许有static,abstract,或override修饰符虚方法不能是私有的,因此不能…

【Vue】如何使用Webpack实现打包操作

一、Webpack介绍 Webpack最主要的作用就是打包操作&#xff0c;由两个核心部分构成分别是“出口”与“入口”。wbepack是现在比较热门的打包工具了&#xff0c;它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。说的直白一点&#xff0c;通过webpac…

RPC(远程过程调用)详解

一、RPC是什么 RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。 二、RPC需要解决的问题 1、Call ID映射 我们怎么告诉远…