代码随想录-Day39

news2024/11/24 0:10:36

62. 不同路径

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

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

问总共有多少条不同的路径?
在这里插入图片描述
输入:m = 3, n = 7
输出:28
在这里插入图片描述

方法一:

  /**
     * 1. 确定dp数组下标含义 dp[i][j] 到每一个坐标可能的路径种类
     * 2. 递推公式 dp[i][j] = dp[i-1][j] dp[i][j-1]
     * 3. 初始化 dp[i][0]=1 dp[0][i]=1 初始化横竖就可
     * 4. 遍历顺序 一行一行遍历
     * 5. 推导结果 。。。。。。。。
     *
     * @param m
     * @param n
     * @return
     */
    public static int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        //初始化
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int i = 0; i < n; i++) {
            dp[0][i] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }

这段代码是使用Java实现解决“机器人行走的路径数目”问题,即在一个m x n的网格中,从左上角(0,0)到右下角(m-1,n-1)有多少种行走路径,每次只能向右或向下移动。该问题可以用动态规划(Dynamic Programming, DP)来解决。

代码解析

  1. 确定dp数组下标含义dp[i][j] 表示到达网格中坐标为(i, j)位置的不同路径数目。

  2. 递推公式:到达(i, j)位置的路径数等于从(i-1, j)位置过来的路径数加上从(i, j-1)位置过来的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1]。这是因为机器人到达(i, j)只能是从(i-1, j)向右走一步或从(i, j-1)向下走一步。

  3. 初始化:边界条件是网格的第一行和第一列,因为从这些边缘出发到它们自身的路径只有一条,即dp[i][0]=1(第一列)和dp[0][i]=1(第一行)。

  4. 遍历顺序:按照从上到下、从左到右的顺序遍历网格,先处理好边界和第一行第一列后,逐行逐列计算内部点的路径数目。

  5. 推导结果:最终,dp[m-1][n-1]即为到达右下角的路径总数。

示例

假设m=3n=3,即一个3x3的网格,按照上述代码执行,dp数组会逐步填充如下:

初始状态(考虑初始化):

1 1 1
1 1 1
1 1 1

迭代填充后(忽略边界,根据递推公式):

1 2 3
1 3 6
1 4 10

最终,dp[2][2] = 10,表示从左上角到右下角有10种不同的行走路径。

通过这个动态规划方法,我们可以高效地解决这类路径计数问题。

方法二:

class Solution {
    public int uniquePaths(int m, int n) {
        // 在二维dp数组中,当前值的计算只依赖正上方和正左方,因此可以压缩成一维数组。
        int[] dp = new int[n];
        // 初始化,第一行只能从正左方跳过来,所以只有一条路径。
        Arrays.fill(dp, 1);
        for (int i = 1; i < m; i ++) {
            // 第一列也只有一条路,不用迭代,所以从第二列开始
            for (int j = 1; j < n; j ++) {
                dp[j] += dp[j - 1]; // dp[j] = dp[j] (正上方)+ dp[j - 1] (正左方)
            }
        }
        return dp[n - 1];
    }
}

这段Java代码是解决“机器人行走的路径数目”问题的另一种实现,采用了一维动态规划数组来减少空间复杂度。给定一个m x n的网格,从左上角(0,0)到右下角(m-1,n-1)有多少种行走路径,每次只能向右或向下移动。下面是代码的详细解析:

代码解析

  1. 初始化一维dp数组:由于计算某个位置的路径数只与其正上方和正左方的路径数有关,因此可以使用一维数组dp来存储到达每列底部的路径总数。数组长度为n,初始化时考虑到第一行的每个位置都只有1条路径(即自己到自己),所以使用Arrays.fill(dp, 1);初始化。

  2. 双层循环遍历:外层循环遍历网格的行(从1开始,因为第一行已经初始化),内层循环遍历网格的列(从1开始,因为第一列在初始化时已设定为1)。

  3. 状态转移方程:对于dp[j],即到达第i行第j列的路径数,可以通过累加左边一格的路径数dp[j - 1]得到,即dp[j] += dp[j - 1];。这样做是因为到达(i, j)的路径可以看作是从(i - 1, j)向下走一条路径加上从(i, j - 1)向右走一条路径的所有组合。

  4. 返回结果:遍历完成后,dp[n - 1]存储了到达右下角的路径总数,直接返回这个值即可。

优势

  • 空间优化:相比于二维数组的解法,这里只使用了一维数组来存储每行的结果,从而将空间复杂度从O(mn)降低到了O(n),对于大规模的m和n来说,这是一个显著的优势。
  • 计算逻辑简洁:通过巧妙地复用一维数组,避免了额外的空间开销,同时也保持了逻辑的直观性。

示例

假设m=3n=3,执行上述代码后,dp数组会经历以下变化:

  • 初始化后:dp = [1, 1, 1]
  • 第一轮外层循环后:dp = [1, 2, 3] (到达第二行各列的路径数)
  • 第二轮外层循环后:dp = [1, 3, 6] (到达第三行各列的路径数,最终结果)

最终返回dp[n - 1] = 6,表示从左上角到右下角有6种不同的行走路径。

63. 不同路径 II

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

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

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

网格中的障碍物和空位置分别用 1 和 0 来表示。
在这里插入图片描述
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右
    在这里插入图片描述
    输入:obstacleGrid = [[0,1],[0,0]]
    输出:1

方法一:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];

        //如果在起点或终点出现了障碍,直接返回0
        if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
            return 0;
        }

        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[0][j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
            }
        }
        return dp[m - 1][n - 1];
    }
}

这段Java代码是用于解决“有障碍的格子”的独特路径问题。给定一个二维数组obstacleGrid,其中0表示可以通过的格子,1表示障碍物。你要从左上角(0,0)移动到右下角(m-1, n-1),每次只能向右或向下移动。该函数计算并返回从起点到终点的不同路径数目,如果无法到达终点则返回0。

代码解析:

  1. 初始化变量:首先获取网格的行数m和列数n,并创建一个与obstacleGrid同样大小的二维数组dp来存储到达每个格子的路径数。

  2. 检查起点和终点:如果终点或起点有障碍物,直接返回0,因为这意味着没有路径可达。

  3. 初始化边界条件:遍历第一列和第一行,如果格子不是障碍物,将其路径数设为1。这表示从起点开始向右或向下有一条路径。

    • 对于第一列:dp[i][0] = 1(若无障碍)
    • 对于第一行:dp[0][j] = 1(若无障碍)
  4. 动态规划填充dp数组:从第二行和第二列开始,对于每个格子(i, j),如果该格子不是障碍物,则其路径数为上面格子(i-1, j)的路径数加上左边格子(i, j-1)的路径数,即dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。如果遇到障碍物,则该位置的路径数设为0,因为不能通过。

  5. 返回结果:最后,dp[m - 1][n - 1]存储了到达终点的路径数目,返回该值作为结果。

示例

假设obstacleGrid为:

[
  [0, 0, 0],
  [0, 1, 0],
  [0, 0, 0]
]

运行这段代码会返回2,因为从左上角到右下角只有两条路径可走,且中间的障碍物阻止了其他可能的路径。

方法二:

// 空间优化版本
class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[] dp = new int[n];

        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                } else if (j != 0) {
                    dp[j] += dp[j - 1];
                }
            }
        }
        return dp[n - 1];
    }
}

这段代码是针对“有障碍的格子”的独特路径问题的空间优化版本。与之前的解决方案相比,这里使用了一维数组dp来代替二维数组,从而节省了空间。代码依然计算从二维数组obstacleGrid的左上角到右下角的无障碍路径数量,其中1表示障碍物,0表示可通过的路径。

代码解析

  1. 初始化变量:获取网格的行数m和列数n,并声明一个一维数组dp,长度为n,用于记录到达当前列的路径数。

  2. 初始化第一行:遍历第一行的列,只要遇到无障碍物(obstacleGrid[0][j] == 0),就将dp[j]设为1,表示从起点到这一列有一条路径。

  3. 动态规划填充dp数组

    • 遍历每一行(从第二行开始),对于每一行中的每一列:
      • 如果当前格子是障碍物,那么到达该格子的路径数为0,因此dp[j] = 0
      • 如果当前格子不是障碍物,有两种情况:
        • 如果是第一列(j == 0),则只能从上方到达,因此dp[j] = dp[j](保持不变,实际上这种情况在循环外已处理,此处默认处理下一列的情况)。
        • 如果不是第一列,到达当前格子的路径数等于上方格子的路径数加上左侧格子的路径数(因为只能向上或向左移动),即dp[j] += dp[j - 1]
  4. 返回结果:最后返回dp[n - 1],即到达终点的路径数量。

优点

  • 空间优化:相比使用二维数组的解法,这里仅使用一维数组存储每行的路径数,将空间复杂度从O(mn)降低到O(n),在处理大尺寸矩阵时更加高效。

示例

假设obstacleGrid为:

[
  [0, 0, 0],
  [0, 1, 0],
  [0, 0, 0]
]

该代码会返回2,表示有两条路径可以从左上角走到右下角,且避开了中间的障碍物。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1862461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gradle学习-2 Groovy

1、Groovy基础语法 1.1、基本数据类型 Groovy支持数据类型&#xff1a;byte, short, int, long, float, double, char &#xff08;1&#xff09;创建一个Android Studio项目 &#xff08;2&#xff09;在根目录新建一个 leon.gradle&#xff0c;输入以下内容 leon.gradle…

Netty学习(二)——黏包半包、协议设计解析、聊天室

一、粘包与半包 1.1 粘包和半包复现 1、粘包复现&#xff1a; Server代码&#xff1a; public class ProblemServer {public static void main(String[] args) throws InterruptedException {new ServerBootstrap()//若是指定接收缓冲区大小&#xff1a;就会出现黏包、半包…

计算机基础知识——面向对象:封装+继承+多态整理

面向对象三大特性&#xff1a;封装、继承、多态。 1.封装 将一系列相关事物的共同的属性和行为提取出来&#xff0c;放到一个类中&#xff0c;同时隐藏对象的属性和实现细节&#xff0c;仅对外提供公共的访问方式。 【JavaBean类就可以看作是封装的完美案例。】 setter和get…

电磁兼容试验数据的单位转换 笔记

1. 单位dB 的介绍 分贝&#xff08;decibel&#xff0c;/dɛsɪ.bɛl/&#xff09;是量度两个相同单位之数量比例的计量单位&#xff0c;主要用于度量声音强度&#xff0c;常用dB表示。 分贝是较常用的计量单位。可表示为&#xff1a; 1. 表示功率量之比的一种单位&#xff0c…

作业6.20

1.已知网址www.hqyj.com截取出网址的每一个部分(要求&#xff0c;该网址不能存入文件中) 2.将配置桥接网络的过程整理成文档&#xff0c;发csdn 步骤i&#xff1a;在虚拟机设置中启用桥接模式 1. 打开VMware虚拟机软件。 2. 选择您想要配置的虚拟机&#xff0c;点击菜单栏中的“…

正版软件 | Copywhiz 6:革新您的文件复制、备份与管理体验

在数字化时代&#xff0c;文件管理的效率直接影响到我们的生产力。Copywhiz 6 最新版本&#xff0c;带来了前所未有的文件处理能力&#xff0c;让复制、备份和组织文件变得轻而易举。 智能选择&#xff0c;只复制更新内容 Copywhiz 6 的智能选择功能&#xff0c;让您只需几次点…

PDF编辑软件pdf转word工具Acrobat DC百度云盘分享

如大家所了解的&#xff0c;Adobe Acrobat DC是一款高级PDF文档编辑和管理软件&#xff0c;它整合了创建、编辑、共享和签署PDF文件的强大功能。这款软件为用户提供了一系列高效的工具&#xff0c;使得处理PDF文件变得异常简单&#xff0c;大幅提升办公效率。 Acrobat DC软件核…

【实用软件】Internet Download Manager(IDM6.41)下载及安装教程

​数据表明但是能够通过搭配下载的方式来使用IDM&#xff08;比如用迅雷离线下载&#xff0c;115离线&#xff0c;百度网盘等离线下载好的资源&#xff0c;然后结合HTTP协议的特性再用IDM下载&#xff09;能够达到事半功倍的效果。有目共睹的是IDM下载HTTP链接十分快&#xff0…

ctr/cvr预估之DeepFM模型

ctr/cvr预估之DeepFM模型 在数字营销的浪潮中&#xff0c;点击率&#xff08;CTR&#xff09;和转化率&#xff08;CVR&#xff09;预估已成为精准广告投放和个性化推荐系统的核心。随着深度学习技术的蓬勃发展&#xff0c;传统的机器学习方法&#xff0c;如逻辑回归和因子分解…

26.高级特性(上)

目录 一、不安全的Rust二、不安全的超能力2.1 概念2.2 解引用裸指针2.3 调用不安全的函数或方法2.3 创建不安全代码的安全抽象2.4 使用extern函数调用外部代码2.5 访问或修改可变静态变量2.6 实现不安全trait2.7 访问联合体中的字段 三、高级trait3.1 关联类型在trait定义中指定…

沙姆镜头标定与重建

沙姆定律&#xff08; Scheimpflug principle&#xff09;则可以保证测量平面的物体能够清晰成像&#xff0c; 因此能够起到调整景深区域位置的作用。Scheimpflug 镜头就是根据沙姆定律所设计的一种特殊的镜头&#xff0c;通过机械结构使镜头与相机本体发生一定程度的偏转&…

网络爬虫Xpath开发工具的使用

开发人员在编写网络爬虫程序时若遇到解析网页数据的问题&#xff0c;则需要花费大量的时间编 写与测试路径表达式&#xff0c;以确认是否可以解析出所需要的数据。为帮助开发人员在网页上直接 测试路径表达式是否正确&#xff0c;我们在这里推荐一款比较好用的 XPath 开发工…

vue:响应式原理解析,深入理解vue的响应式系统

一、文章秒读 vue的响应式系统核心有两个&#xff0c;简单描述就是&#xff1a; 1.在数据变化时重新render依赖相关函数&#xff08;组件&#xff09;。 2.在vue2和vue3中分别使用Object.defineProperty和Proxy进行对象属性的读写。 数据变化时&#xff1a; 二、什么是响应…

123.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-收邮件功能的完善与优化

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

DDD学习笔记一

DDD的基本原则 &#xff08;1&#xff09;保持语言、模型、代码三者一致 语言&#xff1a;开发团队与领域专家沟通使用的自然语言。因为它与设计模型、代码是一致的&#xff0c;所以也称为通用语言。 模型&#xff1a;设计的输出物&#xff0c;是对领域逻辑的精准建模。模型会…

多供应商食品零售商城系统的会员营销设计和实现

在多供应商食品零售商城系统中&#xff0c;会员营销是提升用户粘性和增加销售的重要手段。一个有效的会员营销系统能够帮助平台更好地了解用户需求&#xff0c;提供个性化服务&#xff0c;进而提高用户满意度和忠诚度。本文将详细探讨多供应商食品零售商城系统的会员营销设计与…

LeetCode 算法:二叉树的层序遍历 c++

原题链接&#x1f517;&#xff1a;二叉树的层序遍历 难度&#xff1a;中等⭐️⭐️ 题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;roo…

工控必备C#

微软的C# 语言&#xff1f; QT 熟了以后,Qt 更方便些 方法Signal Slot 感觉上一样 现在更推荐PyQt 来构建,底层还是Qt C 的那些库,Qt 的开源协议有点狗

前端技术栈学习:Vue2、Vue cli脚手架、ElementUI组件库、Axios

1 基本介绍 &#xff08;1&#xff09;Vue 是一个前端框架, 易于构建用户界面 &#xff08;2&#xff09;Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或项目整合 &#xff08;3&#xff09;支持和其它类库结合使用 &#xff08;4&#…

Python——Flask开发框架基础使用介绍

目录 Flask简介 安装 Flask 创建一个简单的 Flask 应用 运行你的Flask应用 添加模板和静态文件 使用静态文件 处理表单和数据 使用 Flask 扩展 结论 Flask简介 Flask 是一个轻量级的 Python Web 框架&#xff0c;它以其简洁和灵活的特点广受欢迎。Flask 让开发者能够快…