LeetCode 热题 HOT 100 打卡计划 | 第十天 | 每日进步一点点

117 阅读1分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

64. 最小路径和

思路

(动态规划) O(m*n)

状态表示: f[i,j]表示从(0,0)走到(i,j)的最小路径和。那么,f[n-1][m-1]就表示从网格左上角到网格右下角的最小路径和,即为答案。

状态转移:

图片.png

由于限制了只会向下走或者向右走,因此到达(i,j)有两条路径

  • 从上方转移过来,f[i][j] = f[i-1][j] + grid[i][j]
  • 从左方转移过来,f[i][j] = f[i][j-1] + grid[i][j]

因此,状态计算方程为: f[i][j] = max(f[i - 1][j], f[i][j - 1]) + grid[i][j], 从向右和向下两条路径中选择路径之和最小的转移过来,再加上grid[i][j]的值。

初始化条件: f[0][0] = grid[0][0], 其余都初始化为正无穷。

时间复杂度分析: O(m*n),其中 m和 n分别是网格的行数和列数 。

c++代码

 class Solution {
 public:
     int minPathSum(vector<vector<int>>& grid) {
         int n = grid.size(), m = grid[0].size();
         vector<vector<int>> f(n + 1, vector<int>(m + 1, INT_MAX));
         f[0][0] = grid[0][0];
         for(int i = 0; i < n; i++)
             for(int j = 0; j < m; j++){
                 if(!i && !j) continue;
                 if(i) f[i][j] = min(f[i - 1][j] + grid[i][j], f[i][j]);
                 if(j) f[i][j] = min(f[i][j - 1] + grid[i][j], f[i][j]);
             }
         return f[n - 1][m - 1];    
     }
 };

70. 爬楼梯

思路

(递推) O(n)

分析题目可以发现:

  • 上 1 阶台阶:有1种方式。
  • 上 2 阶台阶:有1+1和2两种方式。
  • 上 3 阶台阶:到达第3阶的方法总数就是到第1阶和第2阶的方法数之和。
  • 上 n 阶台阶,到达第n阶的方法总数就是到第 (n-1) 阶和第 (n-2) 阶的方法数之和。

因此,定义数组 f[i] 表示上i 级台阶的方案数,则枚举最后一步是上1级台阶,还是上2级台阶,所以有: f[i] = f[i−1]+f[i−2]

图片.png 时间复杂度分析: 递推状态数O(n),转移时间复杂度是 O(1),所以总时间复杂度是 O(n)。

c++代码

 class Solution {
 public:
     int climbStairs(int n) {
         if(n <= 2) return n;
         vector<int>f(n + 1);
         f[1] = 1;
         f[2] = 2;
         for(int i = 3; i <= n; i++){
             f[i] = f[i - 1] + f[i - 2];
         }
         return f[n];
     }
 };

72. 编辑距离

思路

(动态规划) O(n * m)

给你两个单词 word1word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

状态表示: f[i][j] 表示将 word1 的前 i 个字符变成 word2 的前 j 个字符需要进行的最少操作次数。

状态计算:

如何计算f[i][j]?考虑word1的第i个字符与word2的第j个字符(下标从1开始),分为两种情况:

  • 1、word1[i] == word2[j],则f[i][j] == f[i - 1][j - 1];

  • 2、word1[i] != word2[j],我们有三种选择,替换、删除、插入:

    • 替换: 替换word1的第i个字符或者替换word2的第j个字符,则f[i][j] == f[i - 1][j - 1] + 1
    • 删除: 删除word1的第i个字符或者删除word2的第j个字符,则f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;
    • 插入:word2[j] 后面添加 word1[i]或者在word1[i]后添加word2[j],则f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;

初始化:

 for(int i = 0; i <= n; i++)  f[i][0] = i;   //将长度为i的word1变成长度为0的word2需要进行最少i次删除操作
 for(int i = 0; i <= m; i++)  f[0][i] = i;   //将长度为i的word2变成长度为0的word1需要进行最少i次添加操作

时间复杂度分析: 状态数为O(n * m),状态计算为O(1),因此总的时间复杂度为O(n * m)。

c++代码

 class Solution {
 public:
     int minDistance(string word1, string word2) {
         int n = word1.size(), m = word2.size();
         word1 = ' ' + word1;
         word2 = ' ' + word2;
         vector<vector<int>>f(n + 1, vector<int>(m + 1));
         for(int i = 0; i <= n; i++)  f[i][0] = i;  //i次删除
         for(int i = 0; i <= m; i++)  f[0][i] = i;  //i次添加 word1 -> word2
         for(int i = 1; i <= n; i++)
             for(int j = 1; j <= m; j++){
                 f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1; //添加或者删除
                 int t = word1[i] != word2[j];
                 f[i][j] = min(f[i][j], f[i - 1][j - 1] + t); //替换
             }
         return f[n][m];    
     }
 };