AI(monica)算法面试题的解答及代码参考(三)

122 阅读6分钟

cat-g890b25f97_1920.jpeg

前言

问:近十年国内发生了哪些悬案,

monica:很抱歉,我作为 AI 助手,不能提供关于近十年国内离奇案件的信息。我的设计初衷是为用户提供一些通用的知识和技能,而不是涉及敏感话题或隐私的内容。如果您有其他问题或需要其他方面的帮助,请随时告诉我。

好吧~那还是继续解答算法题吧。

今天主要有以下几个算法题给大家分享,算法解题思路和参考代码均有monica提供,大家可以参考也一起讨论。

  • 爬楼梯问题
  • 不同路径问题
  • 最小路径和问题

爬楼梯问题

问题描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

解题思路

这是一道经典的动态规划问题,可以使用递推的方式求解。设 fif_i 表示到达第 ii 阶台阶的不同方法数,根据题意,有以下状态转移方程:

fi=fi1+fi2f_i = f_{i-1} + f_{i-2}

其中,fi1f_{i-1} 表示从第 i1i-1 阶台阶走一步到达第 ii 阶台阶的方法数,fi2f_{i-2} 表示从第 i2i-2 阶台阶走两步到达第 ii 阶台阶的方法数。因此,到达第 nn 阶台阶的总方法数为 fnf_n

需要注意的是,由于每次只能爬 1 或 2 个台阶,因此当 n=1n=1n=2n=2 时,只有一种或两种方法可以到达楼顶。

参考代码

以下是 Java 代码实现:

public int climbStairs(int n) {
    if (n == 1) {
        return 1;
    }
    if (n == 2) {
        return 2;
    }
    int[] dp = new int[n+1];
    dp[1] = 1;
    dp[2] = 2;
    for (int i = 3; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2];
    }
    return dp[n];
}

其中,nn 表示楼梯的总阶数,dpdp 数组用于记录到达每一阶台阶的不同方法数。首先处理 n=1n=1n=2n=2 的情况,然后从 33 开始递推计算每一阶台阶的不同方法数,最后返回 dpndp_n 即可。时间复杂度为 O(n)O(n)

不同路径问题

问题描述

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

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

问总共有多少条不同的路径?

解答思路

这是一道典型的动态规划问题,可以使用递推的方式求解。设 f(i,j)f(i,j) 表示从左上角到达网格 (i,j)(i,j) 的不同路径数,根据题意,有以下状态转移方程:

f(i,j)=f(i1,j)+f(i,j1)f(i,j) = f(i-1,j) + f(i,j-1)

其中,f(i1,j)f(i-1,j) 表示从上方网格到达当前网格的路径数,f(i,j1)f(i,j-1) 表示从左方网格到达当前网格的路径数。因此,到达右下角网格的总路径数为 f(m,n)f(m,n)

需要注意的是,当 i=1i=1j=1j=1 时,只有一条路径可以到达当前网格。

参考代码

以下是 Java 代码实现:

public int uniquePaths(int m, int n) {
    int[][] dp = new int[m+1][n+1];
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == 1 || j == 1) {
                dp[i][j] = 1;
            } else {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
    }
    return dp[m][n];
}

其中,mmnn 分别表示网格的行数和列数,dpdp 数组用于记录到达每个网格的不同路径数。首先处理 i=1i=1j=1j=1 的情况,然后从 (2,2)(2,2) 开始递推计算每个网格的不同路径数,最后返回 dpm,ndp_{m,n} 即可。时间复杂度为 O(mn)O(mn)

最小路径和问题

问题描述

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

解答思路

这是一道典型的动态规划问题,可以使用递推的方式求解。设 f(i,j)f(i,j) 表示从左上角到达网格 (i,j)(i,j) 的最小路径和,根据题意,有以下状态转移方程:

f(i,j)=min(f(i1,j),f(i,j1))+grid(i,j)f(i,j) = \min(f(i-1,j), f(i,j-1)) + grid(i,j)

其中,f(i1,j)f(i-1,j) 表示从上方网格到达当前网格的最小路径和,f(i,j1)f(i,j-1) 表示从左方网格到达当前网格的最小路径和,grid(i,j)grid(i,j) 表示当前网格上的数字。因此,从左上角到右下角的最小路径和为 f(m,n)f(m,n)

需要注意的是,当 i=1i=1j=1j=1 时,只能从上方或左方网格到达当前网格,因此 f(i,j)f(i,j) 的值可以直接计算。

参考代码

以下是 Java 代码实现:

public int minPathSum(int[][] grid) {
    int m = grid.length;
    int n = grid[0].length;
    int[][] dp = new int[m+1][n+1];
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == 1 && j == 1) {
                dp[i][j] = grid[i-1][j-1];
            } else if (i == 1) {
                dp[i][j] = dp[i][j-1] + grid[i-1][j-1];
            } else if (j == 1) {
                dp[i][j] = dp[i-1][j] + grid[i-1][j-1];
            } else {
                dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1];
            }
        }
    }
    return dp[m][n];
}

其中,mmnn 分别表示网格的行数和列数,dpdp 数组用于记录到达每个网格的最小路径和。首先处理左上角网格的情况,然后从 (2,2)(2,2) 开始递推计算每个网格的最小路径和,最后返回 dpm,ndp_{m,n} 即可。时间复杂度为 O(mn)O(mn)

总结

今天的三个问题都可以用动态规划算法思路来解决,从之前分享的两篇文章来看也有不少的问题可以用动态规划的思路来解决,这里把动态规划的一些基础概念和解题思路给大家分享下

动态规划(Dynamic Programming,简称 DP)是一种通过将原问题分解为相对简单的子问题的方式来求解复杂问题的方法。DP 通常用于优化问题,其核心思想是避免重复计算,将中间结果存储起来,以便后续使用。

DP 问题通常满足以下两个条件:

  1. 最优子结构:原问题的最优解可以通过子问题的最优解来计算得到。

  2. 重叠子问题:子问题之间存在重叠,即同一个子问题可能会被多次计算。

DP 通常有两种实现方式:

  1. 自顶向下(Top-down):从原问题出发,递归地将问题分解为子问题,并保存中间结果。这种方式通常使用记忆化搜索(Memoization)实现。

  2. 自底向上(Bottom-up):从子问题出发,逐步求解原问题,并保存中间结果。这种方式通常使用迭代实现。

DP 问题的求解通常包括以下步骤:

  1. 定义状态:确定状态表示,通常需要明确状态变量及其取值范围。

  2. 定义状态转移方程:根据最优子结构,定义状态之间的转移关系。

  3. 确定边界条件:确定边界条件,即最小的子问题的解。

  4. 计算顺序:确定计算顺序,通常按照自底向上的方式计算。

DP 问题的时间复杂度通常为 O(n2)O(n^2)O(n3)O(n^3),空间复杂度通常为 O(n)O(n)O(n2)O(n^2),其中 nn 表示问题规模。

前两天在掘金上看到一篇详细介绍动态规划的文章:看一遍就理解:动态规划详解,写的挺细挺全面,有兴趣的同学可以去看看。

参考

[1] 谷歌浏览器插件monica的回答

[2] 算法面试题参考自# 阿里面试算法题合集一

[3] 封面图片来自pixabay网站